【codevs1297】硬幣 完全揹包
阿新 • • 發佈:2018-11-29
題目大意:給定 N 種不同種類的硬幣,每種硬幣的重量範圍在一個可變區間內,但是價值恆定,求給定一個重量 W,求有多少種面值不同的組合方式。
題解:如果硬幣的重量恆定,那麼就是一道裸的完全揹包問題。因此,可以先將給定的硬幣拆分成多個重量不同的硬幣。
總的來說,這道題所求的是目標狀態有多少種可能的解,而不是最優解,因此有以下兩種方式。
解法1:在每個狀態中維護一個 \(STL--set\),用來儲存到達該狀態所有可能的值,最後輸出集合的大小即可,常數較大。
解法2:將問題轉化為判定性問題,即:額外增加一維用來表示當前可能的價值。
程式碼 1 如下
#include <bits/stdc++.h> using namespace std; int w,n,ans,val[300],cost[300],tot; set<int> dp[110]; void read_and_parse(){ scanf("%d%d",&w,&n); for(int i=1,v,mi,mx;i<=n;i++){ scanf("%d%d%d",&v,&mi,&mx); for(int j=mi;j<=mx;j++)cost[++tot]=j,val[tot]=v; } } void solve(){ dp[0].insert(0); for(int i=1;i<=tot;i++) for(int j=cost[i];j<=w;j++) for(set<int>::iterator p=dp[j-cost[i]].begin();p!=dp[j-cost[i]].end();p++) dp[j].insert(*p+val[i]); printf("%d\n",dp[w].size()); } int main(){ read_and_parse(); solve(); return 0; }
程式碼 2 如下
#include <bits/stdc++.h> using namespace std; int w,n,ans,val[300],cost[300],tot,dp[110][2510]; void read_and_parse(){ scanf("%d%d",&w,&n); for(int i=1,v,mi,mx;i<=n;i++){ scanf("%d%d%d",&v,&mi,&mx); for(int j=mi;j<=mx;j++)cost[++tot]=j,val[tot]=v; } } void solve(){ dp[0][0]=1; for(int i=1;i<=tot;i++) for(int j=cost[i];j<=w;j++) for(int k=val[i];k<=2500;k++) dp[j][k]|=dp[j-cost[i]][k-val[i]]; for(int i=0;i<=2500;i++)if(dp[w][i])++ans; printf("%d\n",ans); } int main(){ read_and_parse(); solve(); return 0; }