点击打开链接
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 2865 | Accepted: 794 |
Description
Input
Output
Sample Input
12 5 3 1 2 16 0 0 0 1 0 0 0 0 0
Sample Output
Throw in 2 cents, 2 nickels, 0 dimes, and 0 quarters. Charlie cannot buy coffee.题意:给你一个整数p,告诉你价值为1,5,10,25的硬币的数量,让输出价值为p并且硬币数量最多的方案。
咋一看,就是一个多重背包,再记录一下路径就行了,但是将物品加上logn的优化以后任然超时,其实这个题有O(4*p)的算法。
首先:dp[ i ][ j ],表示前i种硬币已经选了价值为j所用的硬币最多有多少个,use[ i ][ j ],表示dp[ i ][ j ]状态下第i种硬币用了多少个,如果我们从小到大的选择硬币,那么因为dp[ i ][ j ]已经保证了硬币是最多的,因为第i中硬币比前面的大,所以第i种硬币一定是能达到条件的情况下最小的,即,use[ i ][ j ]为状态dp[ i ][ j ]下第i种硬币最少使用多少个。那么状态转移方程就是:dp[ i ][ j ]=max(dp[ i-1 ][ j ],dp[ i-1 ][ j -xi]+1),dp[ i ][ j ]=max(dp[ i ][ j ],dp[ i ][ j-xi]+1)且(use[ i ][ j-xi] +1<=ci]),因为use[ i ][ j ]一定是最小的,所以这样转移一定是最优的。。。代码如下:
#include<stdio.h> #include<iostream> #include<string> #include<string.h> #include<vector> #include<algorithm> #include<queue> #include<stack> #define nn 110 #define inff 0x3fffffff #define mod 1000000007 #define eps 1e-9 using namespace std; typedef long long LL; int p; int c[10]; int dp[5][11000]; int use[5][11000]; int qian[10]; int num[10]; int main() { int i,j; qian[1]=1,qian[2]=5,qian[3]=10,qian[4]=25; while(scanf("%d%d%d%d%d",&p,&c[1],&c[2],&c[3],&c[4])!=EOF&&p+c[1]+c[2]+c[3]+c[4]) { memset(use,0,sizeof(use)); for(i=0;i<=4;i++) { for(j=0;j<=p;j++) dp[i][j]=-inff; } dp[0][0]=0; for(i=1;i<=4;i++) { for(j=0;j<=p;j++) { dp[i][j]=dp[i-1][j]; if(j-qian[i]>=0) { if(dp[i][j]<dp[i-1][j-qian[i]]+1&&c[i]>=1) { dp[i][j]=dp[i-1][j-qian[i]]+1; use[i][j]=1; } if(dp[i][j]<dp[i][j-qian[i]]+1&&use[i][j-qian[i]]+1<=c[i]) { dp[i][j]=dp[i][j-qian[i]]+1; use[i][j]=use[i][j-qian[i]]+1; } } } } if(dp[4][p]<0) { puts("Charlie cannot buy coffee."); continue; } memset(num,0,sizeof(num)); int ix=4; int fc=p; while(ix) { num[ix]=use[ix][fc]; fc-=qian[ix]*num[ix]; ix--; } printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",num[1],num[2],num[3],num[4]); } return 0; }