[二維費用揹包]FATE HDU
阿新 • • 發佈:2018-12-16
acm.hdu.edu.cn/showproblem.php?pid=2159
題意:現在玩遊戲欲升級,升級需要經驗值n,殺怪可以賺經驗值,但是會扣忍耐度,遊戲中有k種怪,數目都無限多。現在玩家還有m點忍耐度,問能否在最多殺s個怪的情況下升級,若能則輸出剩餘的最大忍耐度。 思路: 1、此題有兩個約束,一個是忍耐度,一個是最多殺的怪物數。抽象來說意味著對於每件物品,具有兩種不同的費用。此即為二維揹包的模型!而二維費用揹包模型最常見的形式便是:物品總個數有上限限制,如此題的最多殺s個怪。而關於二維揹包的處理,無非是增加一個狀態維度即可,轉移方程類比著選或不選第i個物品的思路寫出即可。 2、此題怪物數目無限多,也即這是個完全揹包問題。關於完全揹包問題,存在O(N*V)時間複雜度的演算法(N為物品總數,V為揹包容量), 實現方式即為 f[i,j]=max(f[i-1,j],f[i,j-v[i]]+w[i]) 其中迴圈變數i遍歷物品種類,j正向遍歷0~V 注意遞推式與01揹包有些許不同: (1)max的第二項是f[i,...],是i!
(2)j在01揹包時是逆序迴圈的,如今變成了順序迴圈。 這樣的目的是,該子結果就是已經考慮過重複選第i項的結果了!另外這個i和j如果需要是可以交換迴圈順序的。
當然依然可以空間優化,去除物品種類那一維。而值得注意的是此題需要算的是如果可以升級需輸出能剩餘的最大忍耐度,故迴圈時可以將忍耐度那一維放在最外層迴圈
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #include <map> #include <queue> #include <set> #include <stack> using namespace std; typedef long long ll; const int maxn_m = 100 + 5; const int maxn_n = 100 + 5; const int maxn = 2000000+5; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; const double PI = acos(-1.0); int dp[110][110][110];// 種類 耐力 殺敵個數 int val[110],w[110];// 經驗 耐力 int main () { int n,k,m,s,ans=INF; while(scanf("%d%d%d%d",&n,&m,&k,&s)!=EOF){ memset(dp,0,sizeof(dp)); for(int i=1;i<=k;i++)scanf("%d%d",w+i,val+i); for(int i=1;i<=k;i++) for(int j=1;j<=m;j++)// 這裡的 把每層都傳下去 丟了val[i] 前的wa也沒有啥解釋的 for(int t=1;t<=s;t++){ dp[i][j][t]=(j<val[i])?dp[i-1][j][t]:max(dp[i][j-val[i]][t-1]+w[i],dp[i-1][j][t]); ans=(dp[i][j][t]>=n)?min(ans,j):ans; } if(dp[k][m][s]<n) printf("-1\n"); else printf("%d\n",m-ans); ans=INF; } return 0; }
外帶一個 二維優化版
dp[i][k]表示在i忍耐度下殺k只怪所得到的最多經驗;
dp[i][k]=max(dp[i][k],dp[i-a[j].w][k-1]+a[j].v)
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #include <map> #include <queue> #include <set> #include <list> using namespace std; typedef long long ll; const int maxn = 105; const int mod = 1e9+7; const int INF = 0x3f3f3f3f; const double PI=acos(-1.0); int dp[maxn][maxn],a[maxn],b[maxn]; int n,m,k,s; int main(){ while(cin>>n>>m>>k>>s){ for(int i=0;i<k;i++) cin>>a[i]>>b[i]; memset(dp,0,sizeof(dp)); int ans=INF; for(int i=0;i<k;i++){ for(int j=b[i];j<=m;j++){ for(int t=1;t<=s;t++){ dp[j][t]=max(dp[j][t],dp[j-b[i]][t-1]+a[i]); if(dp[j][t]>=n){ ans=min(ans,j); } } } } if(ans>m) cout<<-1<<endl; else cout<<m-ans<<endl; } return 0; }