01揹包
動態規劃是一種高效的演算法。在數學和電腦科學中,是一種將複雜問題的分成多個簡單的小問題思想 ---- 分而治之。因此我們使用動態規劃的時候,原問題必須是重疊的子問題。運用動態規劃設計的演算法比一般樸素演算法高效很多,因為動態規劃不會重複計算已經計算過的子問題。因為動態規劃又可以稱為「記憶化搜尋」。
01揹包是介紹動態規劃最經典的例子,同時也是最簡單的乙個。我們先看看01揹包的是什麼?
問題(01揹包):有n個重量和價值分別為vi和ci的物品。從這些物品中挑出總重量不超過m的物品,求所有挑選方案中價值總和的最大值。
這就是被稱為01揹包的問題。在沒學習動態規劃之前,我們看到這個問題第一反應會用dfs搜尋一遍。那我們先使用這種方法來求解01揹包問題:
#include#includeusing
namespace
std;
const
int n=1e3+1
;int
n,m,v[n],c[n];
//從第i個物品開始挑選總重量不大於sumv的部分
int dfs(int now,int
sumv)
intmain()
/*期望得分:30分
*/
乍一看dfs好像就可以解決這個問題,那還有動態規劃什麼事。然而我們仔細分析一下時間複雜度,每一種狀態都用選或者不選兩種可能。所以我們可以得出使用dfs的時間複雜度為o(2^n)。顯然這個方法不是乙個很好的方法,因為這個時間複雜度太高了。我們仔細研究可以發現,造成時間複雜度這麼高的原因是重複計算。既然我們找到複雜度這麼高的原因,那我們就可以想辦法減少它重複計算的次數。仔細分析容易想到,使用乙個二維陣列來記錄每一次搜尋的答案,這樣我們就避免了重複計算。
這樣的小技巧,我們稱之為記憶化搜尋。我們只是小小的改變就讓它的時間複雜度降低至o(nw)。
#include#include#include
using
namespace
std;
const
int n=1e3+1
;int n,m,v[n],c[n],dp[n][n*10];//
儲存每一次搜尋的答案
int dfs(int now,int
sumv)
intmain()
/*期望得分:60-80分
*/
仔細分析,可以發現我們還可以有更簡單的寫法(轉成遞推):
#include#include#include
using
namespace
std;
const
int n=1e3+1
;int n,m,v[n],c[n],dp[n][n*10
];//
dp[i][j] 表示取了i個物品挑選出總重量不超過j的物品時,揹包中的最大價值
intmain()
}printf("%d
",dp[1
][m]);
return0;
}/*期望得分:60-80分
*/
然後dp[i][j]的僅由dp[i+1][j]||dp[i+1][j-v[i]]轉移而來,於是我們可以滾動第一維:
#include#include#include
using
namespace
std;
const
int n=1e3+1
;int n,m,v[n],c[n],dp[2][n*10
];int
main()
}printf("%d
",dp[now][m]);
return0;
}/*期望得分:100分
*/
凡是可以滾動的,必定可以降維。——shenben
#include#include#include
using
namespace
std;
const
int n=1e3+1
;int n,m,v[n],c[n],dp[n*10
];int
main()
}printf("%d
",dp[m]);
return0;
}/*期望得分:100分
*/
使用遞推方程直接求解的方法,我們稱之為dp。因為他每一次的選取,都在動態的計算最優的情況。當然可能他區域性不是最優,但是整體一定是最優解。這就是他和貪心演算法最大的不同,貪心演算法,每一次都是最優,但是整體不一定不是最優。
多重揹包
問題(多重揹包):
就是乙個0,1,2……k揹包(往01揹包上想就好了)有n種重量和價值分別為vi和ci的物品,每種物品有si個。從這些物品中挑出總重量不超過m的物品,求所有挑選方案中價值總和的最大值
搜尋
//期望得分:30-70分
#include#include
#define n 10100
using
namespace
std;
intv[n],c[n],s[n],n,m;
intans;
void dfs(int now,int sumv,int
sumc)
dfs(now+1
,sumv,sumc);
for(int i=1;i<=s[now];i++) dfs(now+1,sumv+i*v[now],sumc+i*c[now]);
}int
main()
記憶化搜尋
//期望得分:60-100分
#include#include
#define n 10100
using
namespace
std;
intv[n],c[n],s[n],n,m;
intans;
int dp[101
][n];
int dfs(int now,int
sumv)
return
dp[now][sumv];
}int
main()
記憶化搜尋轉遞推
//期望得分:60-100分
#include#include
#define n 10100
using
namespace
std;
intv[n],c[n],s[n],n,m;
intans;
int dp[101
][n];
intmain()}}
printf("%d
",dp[1
][m]);
return0;
}
滾動陣列(滾動第一維)
//期望得分:100分
#include#include
#define n 10100
using
namespace
std;
intv[n],c[n],s[n],n,m;
intans;
int dp[2
][n];
intmain()}}
printf("%d
",dp[now][m]);
return0;
}
降維(用二進位制優化)
//期望得分:100分
#include#include
using
namespace
std;
inline
intread()
while(ch>='
0'&&ch<='9')
return x*f;
}const
int n=1e5+10
;int n,m,cnt,xp[30
];int f[n*200
],v[n],c[n];
intmain()
if(z>0
) }
for(int i=1;i<=cnt;i++)
}printf("%d
",f[m]);
return0;
}
其他揹包dp自行整理
揹包 DP 揹包
揹包 題目 是dp中較為常見的題目 分為 0 1 揹包 完全揹包 和多重揹包 這三類 是越來越深入的首先來介紹一下 0 1揹包 首先 0 1 揹包的含義是 給你乙個容量位m的揹包 然後給你n個物品 每個物品具有一定價值和一定重量 會站一定的揹包空間 答案是在n個物品中那幾個 然後使得到的價值最大 首...
揹包dp之01揹包
現在我們有n個配件,他們有不同的價值.但是我們揹包的容量是有限的,因為我們只有乙個一級包,所以我們最多可以裝v重量的東西.但是為了能更好的吃到雞 不存在的 我們要攜帶更有價值的配件,請問我們最多能拿多少價值的配件來當快遞員呢?輸入的第一行是t,表示有一共要打t場比賽.每組資料由三行組成.第一行包含兩...
dp 揹包之多重揹包
問題 多重揹包也是 0 1 揹包的乙個變式。與 0 1 揹包的區別在於每種物品有ki個,而非乙個。解決方案 將k個相同的物品,看作k個不同的物品,但是wi,ci都一樣。即可套用 01揹包方案 詳見 優化方法 二進位制優化 設k個物品分成 a xx a xx 1 a xx k 1 個物品。那麼 a x...