题目:
动态规划版的硬币交换问题,求用最少的不同价值的硬币总数来换给出的硬币总数,如硬币只有1,6,10时,
按贪心算法做就是先给价值为10的硬币,再给两枚价值为1的硬币,总硬币数为3,可是只需要两枚价值为6的
硬币即可
分析:
用dp[i][j]来记录当前的硬币价值总数为j时需要的硬币最小数,i表示从硬币i到n的种类,j表示剩余的未
交换的硬币价值总数,则状态转移方程为
for(int j=0;j<=n;j++)
dp[i][j] = j;
dp[i][j] = dp[i+1][j] 当denom[i]>j时
= min{dp[i+1][j],dp[i][j-denom[i]]+1}当j>=denom[i]时
#include <iostream>
#include <cstring>
using namespace std;
#define X 100
int dp[X][X];
int denom[X];
int use[X][X];
void print(int i,int j)
{ //递归打印使用过的硬币
if(!j)
return;
if(use[i][j])
{//如果为真,即是使用了硬币i,打印并且递归剩余硬币总数j-denom[i]
cout<<denom[i]<<endl;
print(i,j-denom[i]);
}
else//否则,则是使用了下面种类的硬币,递归剩余价值的硬币
print(i+1,j);
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
int n,money;
int cnt = 1;
while(cin>>n)
{
memset(use,false,sizeof(use));
for(int i=1;i<=n;i++)
cin>>denom[i];
cin>>money;
///////////////////初始化
for(int i=0;i<=money;i++)
{
dp[n][i] = i;
use[n][i] = true;
}
for(int i=n-1;i>0;i--)
//由于求dp[i][j]需要知道下一个i或者前面的j,所以j从后面递归到前面,i从前面递归
for(int j=0;j<=money;j++)
{
if(j<denom[i]||dp[i+1][j]<dp[i][j-denom[i]]+1)
{//如果剩余的总价值价值小于当前种类的硬币价值或者不使用当前的硬币而是用后面价值的硬币总数更少
dp[i][j] = dp[i+1][j];
use[i][j] = false;//记录当前位置的硬币和总价值为假,表示当前未使用该硬币
}
else//当总价值大于当前的硬币且使用当前硬币总硬币数小于不使用当前硬币时
{
dp[i][j] = dp[i][j-denom[i]]+1;//更新dp
use[i][j] = true;//表示用了
}
}
cout<<"case "<<cnt++<<endl<<dp[1][money]<<endl;
cout<<"用了的硬币:"<<endl;
print(1,money);
cout<<endl;
}
return 0;
}