暑假-动态规划 III-(X - Cash Machine)

/* 
题意: 
     有各种不同面值的货币,每种面值的货币有不同的数量,请找出利用这些 
     货币可以凑成的最接近且小于等于给定的数字cash的金额。 
思路:
     多重背包(01背包,完全背包,多重背包(2进制原理)2种背包的转换
	 dp[i]表示能用所给硬币凑到最接近i的数目
*/  
#include<iostream>  
using namespace std;  
const int MAXN=100005;  
int mymax(int a,int b)
{
	return a>b?a:b;
}
struct Node  
{  
    int n;//硬币个数  
    int d;//硬币面值  
};  
Node coins[15];//数据录入  
int dp[MAXN];//dp[i]表示能用所给硬币凑到最接近i的数目
//当dp[i]==i时,表示刚好可以用所给硬币凑到数目i
//并且dp[i]一定小于等于i
void CompletePack(int w,int m)  //完全背包(顺序求解,w即使重量也是价值)  
{
	 for(int i=w;i<=m;i++)  
    {  
        dp[i]=mymax(dp[i],dp[i-w]+w);  
    }  
}  
void ZeroOnePack(int w,int m)  //01背包(逆序求解,w即使重量也是价值) 
{  
	 for(int i=m;i>=w;i--)  
    {  
        dp[i]=mymax(dp[i],dp[i-w]+w);  
    }  
}  
void MuiltPack(int w,int c,int m)//
{
	 if(w*c>=m)//面值*数目〉所要凑得面值(相当于有无限多个面值为w的硬币可用)  
    {  
        CompletePack(w,m);//多重背包-->完全背包  
    }  
    else//否则,多重背包-->01背包  
    {   //二进制原理转换成01背包求解。  
        int k=1;//k从1.2.4.....
        while(c-k>0)  
        {  
            ZeroOnePack(w*k,m);
            c-=k;  
            k*=2;  
        }  
        ZeroOnePack(w*c,m);//剩下的硬币数目
    }  
}  
int main()  
{  
    int cash,N;//所给要求的面值和硬币数量  
    while(cin>>cash>>N)  
    {  
        memset(dp,0,sizeof(dp));//初始化  
        for(int i=1;i<=N;i++)//数据录入  
        {  
            cin>>coins[i].n;  //硬币个数
            cin>>coins[i].d;  //硬币价值
        }
		if(!cash||!N)//只要有一个为0,则答案就是0  
        {  
            cout<<0<<endl; 
			continue;
        }
        for(int i=1;i<=N;i++)  
        {  
            MuiltPack(coins[i].d,coins[i].n,cash); 
        }  
        cout<<dp[cash]<<endl; 
    }  
    return 0;  
}


你可能感兴趣的:(动态规划)