找钱问题
找钱问题描述:
与背包问题不同,找钱问题是结果必须是把容量全部装满
一.用的钱的最大最小数目
把空间开大,所需求的dp值只是其中的一种特殊情况而已
1.01背包模型,每种货币只能用一次
最小求法
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[50000],w[100]; const int MAX = 50000; const int inf = 0x3f3f3f3f; int n,m; int main() { while(~scanf("%d%d",&n,&m)){ for(int i = 1;i <= n ; i++) scanf("%d%d",&w[i]); for(int i = 1; i <= MAX ;i++) dp[i] = inf; dp[0] = 0; for(int i = 1; i <= n; i++){ for(int j = MAX ; j >= w[i] ;j--){ dp[j] = min(dp[j],dp[j-w[i]]+1); } } if(dp[m] == inf) printf("-1\n"); else printf("%d\n",dp[m]); } return 0; }
最大求法 只要将初始化变成-1,min改成max
2.完全背包模型,每种货币无限使用
把第二个for循环倒一下就行
3.多重背包模型,每一种货币都有数目限制
1.用完全背包和01背包
2.只用01背包*
用二进制来降低复杂度,应为所有的情况都可以用这些二进制的组合来表示,放大拆开,只要放大取出就行
最小求法,最大同理
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[50000],w[100],c[100]; const int MAX = 50000; const int inf = 0x3f3f3f3f; int n,m; void oneback(int cost,int cnt) { for(int i = MAX; i >= cost; i--) dp[i] = min(dp[i-cost]+cnt, dp[i]); } int main() { int n,m; while(~scanf("%d%d",&n,&m)){ for(int i = 1; i <= n ; i++) scanf("%d%d",&w[i],&c[i]); for(int i = 1; i <= MAX;i ++) dp[i] = inf; dp[0] =0; int k; for(int i = 1; i <= n ; i++){ k = 1; while(k < c[i]){ oneback(k*w[i],k); c[i] -= k; k*= 2; } oneback(w[i]*c[i],c[i]); } if(dp[m] == inf) printf("-1\n"); else printf("%d\n",dp[m]); } return 0; }
4.多重背包模型,钱多了要找回(找回的时候是完全背包,不限数目)
最小求法
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[50000],w[100],c[100]; const int MAX = 50000; const int inf = 0x3f3f3f3f; int n,m; void oneback(int cost,int cnt) { for(int i = MAX; i >= cost; i--) dp[i] = min(dp[i-cost]+cnt, dp[i]); } void comback(int cost,int cnt) { for(int i = MAX+cost ; i >= 0 ; i--) dp[i] = min(dp[i-cost]+cnt,dp[i]); } int main() { int n,m; while(~scanf("%d%d",&n,&m)){ for(int i = 1; i <= n ; i++) scanf("%d%d",&w[i],&c[i]); for(int i = 1; i <= MAX;i ++) dp[i] = inf; dp[0] =0; int k; for(int i = 1; i <= 2*n ; i++){//这样写方便,没有什么特殊的含义。。 if(i <= n){ k = 1; while(k < c[i]){ oneback(k*w[i],k); c[i] -= k; k*= 2; } oneback(w[i]*c[i],c[i]); } } else comback(-w[i-n],1); } if(dp[m] == inf) printf("-1\n"); else printf("%d\n",dp[m]); } return 0; }
完全背包是从小到大的,但是负数,把循环方向改变一下。循环条件的该表只是要适应下面dp值的改变
5.记录路径的多重背包模型(完全背包和01背包只要把if条件改一下)
最大求法
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[10000],w[10000],num[10000],t[10000],path[10000],ans[10000]; const int MAX = 50000; const int inf = 0x3f3f3f3f; int n,m; int main() { while(~scanf("%d%d",&n,&m)){ for(int i = 1; i <= n ; i++) scanf("%d%d",&w[i],&t[i]); memset(dp,0,sizeof(dp)); dp[0] = 1; for(int i = 1; i <= n ; i++){ for(int j = w[i]; j <= m; j++){ if(dp[j-w[i]] && dp[j-w[i]] + 1 > dp[j] && num[j-w[i]] < t[i]){ dp[j] = dp[j-w[i]]; num[j] = num[j-w[i]] + 1; path[j] = j - w[i]; } } } int i = m; if(dp[m] > 0){ while(i!=0){ ans[i-path[i]]++; i = path[i]; } for(int i = 1; i <= n ;i++){ if(ans[i]){ printf("%d:%d ",i,ans[i]); } } } } return 0; }
path记录的是当前有j钱的时候买了一件东西之后的最优路径,因为完全背包最后解肯定是最优的所以倒推是正确的
dp只是用来判断,那个值并不能用
最小求法
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int v[5] = {0,1,5,10,25}; int dp[10010],ans[10010],num[10010],path[10010],t[5]; int p; const int inf = 0x3f3f3f3f; int main() { while(~scanf("%d",&p)){ for(int i = 1; i <= 4; i++) scanf("%d",&t[i]); if((p+t[1]+t[2]+t[3]+t[4]) == 0) break; memset(ans,0,sizeof(ans)); memset(path,0,sizeof(path)); for(int i = 1; i <= 10010; i++) dp[i] = inf; dp[0] = 1; for(int i = 1; i <= 4; i++){ memset(num,0,sizeof(num)); for(int j = v[i]; j <= p; j++){ if(dp[j-v[i]] != inf && dp[j-v[i]] + 1 < dp[j] && num[j-v[i]] < t[i]){ dp[j] = dp[j-v[i]] + 1;//使得后面每一个状态都是从前面一个得到,并且满足两个条件1:用去一个后的数目要比没用去的多2:用去的硬币数目不超过硬币本身 num[j] = num[j-v[i]] + 1; path[j] = j - v[i];//难想到。。用来记录路径 } } } int i = p; if(dp[p]!=inf){ while(i!=0){ ans[i-path[i]]++;//i-path[i] = i - ( i - v[i]) = v[i] i = path[i];//path[i]表示有i钱的时候用了一种硬币之后的钱的数目 } printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]); } else printf("Charlie cannot buy coffee.\n"); } return 0; }
6.