動態規劃DP-揹包問題-ACM
阿新 • • 發佈:2019-01-05
AC code: #include<bits/stdc++.h> using namespace std; const int maxn = 125; int dp[maxn+5][maxn + 5]; int n = 0; void solve(){ fill(dp[0],dp[0]+(maxn+5)*(maxn+5),0);//詳情見揹包九講的初始化問題 for(int i = 0;i <= maxn;++i){//初始化,這是合法狀態 dp[1][i] = 1; } for(int i = 1;i <= maxn;++i){//恰好裝滿 dp[i][0] = 1; } for(int i = 2;i <= maxn;++i){ for(int j = 1;j <= maxn;++j){ dp[i][j] = dp[i-1][j] + (j >= i ? dp[i][j-i]:0); } } } int main(){ solve(); while(~scanf("%d",&n)){ printf("%d\n",dp[n][n]); } return 0; }
AC code: #include<bits/stdc++.h> using namespace std; const int maxn = 300; int dp[18][maxn + 5];//dp[i][j]表示使用前i種貨幣,湊出j元的方法數 int n = 0; void solve(){ fill(dp[0],dp[0]+18*(maxn+5),0); for(int i = 0;i <= maxn;++i){ dp[1][i] = 1; } for(int i = 1;i <= 17;++i){//要初始化為1 dp[i][0] = 1; } for(int i = 2;i <= 17;++i){ for(int j = 1;j <= maxn;++j){ dp[i][j] = dp[i-1][j] + (j >= i*i? dp[i][j-i*i]:0);//i,j狀態由只是用前i-1種貨幣 //湊成j元的方法數+使用前i種貨幣j-i元(在加上i元貨幣)的方法數 } } /* for(int i = 1;i <= 4;++i){ for(int j = 0;j <= 4;++j){ printf("%d%c",dp[i][j],(j == 10)?'\n':' '); } } */ } int main(){ solve(); while(~scanf("%d",&n) && n != 0){ printf("%d\n",dp[17][n]); } return 0; }
上面兩個問題的初始化是一樣的。揹包九講有段話話很重要
初始化的細節問題:
我們看到的求解最優解的揹包問題中,事實和桑有兩種不太相同的問法。
- 要求”揹包恰好裝滿“ 時的最優解
- 不要求揹包一定要被裝滿時的最優解
我們上面所討論的就是第2種, 不要求揹包一定要被裝滿時的最優解。
一種區別這兩種問法的實現方法是在初始化的時候有所不不同。
如果是第一種問法,要求恰好裝滿揹包,那麼在初始化時除了 dp[0]dp[0] 為0, 其他dp[1…W]均設為−∞dp[1…W]均設為−∞ ,這樣就可以保證最終得到 dp[W]dp[W] 是一種恰好裝滿揹包的最優解
如果並沒有要求必須把揹包裝滿,而是隻希望價格儘量大,初始化時應該將dp[0…W]dp[0…W] 全部設為0。
這是為什麼呢?可以這樣理解:初始化的dpdp 陣列事實上就是在沒有任何物品可以放入揹包時的合法狀態。如果要求揹包恰好裝滿,那麼此時只有容量為0的揹包可以在什麼也不裝的狀態下被 “恰好裝滿” ,此時揹包價值為0。其他容量的揹包均沒有合法的解,屬於未定義的狀態,所以都應該被賦值為 −∞−∞ 。當前的合法解,一定是從之前的合法狀態推得的
如果揹包並非必須被裝滿,那麼任何容量的揹包都有一個合法解 “什麼也不裝”,這個解的價值為0,所以初始化時狀態的值也就全部為0了。
接下來還是一個多重揹包的問題:https://vjudge.net/contest/263202#problem/C
AC code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 8e3 ;
int num[4];
int val[4] = {0,1,2,5};
bool dp[maxn+10];
void solve(){
int ma = num[1] + 2*num[2] + 5*num[3];
fill(dp,dp+maxn+10,false);
dp[0] = true;
for(int i = 1;i <= 3;++i){
for(int k = 1;k <= num[i];++k){
for(int j = ma;j >= 1;--j){
dp[j] = (dp[j] || dp[j-val[i]]);
//cout << i << " " << j << " " << dp[j] << endl;
}
}
}
for(int i = 1;i <= ma+1;++i){
if(dp[i] == false){
printf("%d\n",i);
return ;
}
}
}
int main(){
while(~scanf("%d %d %d",&num[1],&num[2],&num[3])){
if(num[1] == 0 && num[2] == 0 && num[3] == 0){
break;
}
solve();
}
return 0;
}
AC code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100 + 10;
int dp[maxn][maxn][maxn];//dp[i][j][k]表示使用了i種水果,有了j個盤,在第i種水果使用了k個
struct node{
int mi,ma;
};
node num[maxn];
#define INF 1e6
int n = 0,m = 0;
void solve(){
fill(dp[0][0],dp[0][0]+maxn*maxn*maxn,0);
for(int i = 1;i <= m;++i){//非法狀態初始化為-1
dp[0][i][0] = -INF;
}
for(int i = num[1].mi;i <= num[1].ma;++i){//第一個盤子的初始化
dp[1][i][i] = 1;
}
for(int i = 2;i<= n;++i){
for(int j = 0;j <= m;++j){
for(int k = num[i].mi;j +k <= m && k <= num[i].ma;++k){
for(int l = num[i-1].mi;l <= num[i-1].ma;++l){
if(dp[i-1][j][l] == -INF){
continue;
}
dp[i][j+k][k] += dp[i-1][j][l];//由第i-1種轉移而來,所以要遍歷i-1種的情況
}
}
}
}
int ans = 0;
for(int i = num[n].mi;i <= num[n].ma;++i){//第n種水果使用了i個的種數
ans += dp[n][m][i];//答案要把他們加起來
}
printf("%d\n",ans);
}
int main(){
while(~scanf("%d %d",&n,&m)){
for(int i = 1;i <= n;++i){
scanf("%d %d",&num[i].mi,&num[i].ma);
}
solve();
}
return 0;
}