动态规划01背包3
2013-02-19 14:39 Dr.Ray 阅读(164) 评论(0) 收藏 举报输入货币种类数v,金额n,和v个币值,求组成金额n的货币搭配种类数。
关键是用opt状态数组来储存种类数,其他和01背包一样。这次的状态转移方程是我自己写出来的,比较有成就感!
另外注意钱币数量无限制,所以第二层循环要用顺推而非倒推。
阶段:用来组合的币值的种类数
状态:当前金额的组合种类数
状态转移方程:
opt[j] += opt[j-w[i]] w[i]为币值。
状态转移方程推导经验小结:
opt[j] += opt[j-w[i]]是怎么来的?
opt[j-w[i]]是去掉一个钱币的金额的组合数,因为只是多了一个币,所以用j-w[i]组成j的组合数就是opt[j-w[i]],于是就有opt[j] += opt[j-w[i]]
一开始我是想用 opt[j] += opt[j-w[i]]*opt[w[i]],想法如下:随着递推,金额等于币值的组合数的随之变化,得到币值组合数乘以opt[j-w[i]]就是所有可能。
这带来两个问题,
1、币值的组合数会随着i的变化而变化,比如只有 1、2币值时,5组合数为3,但是w[i]=5时,5的组合数变成4了,那么你在第二层装包过程中只要在用到5组合数位3 时就是错的。在每个金额等于币值时把所有币值遍历一遍貌似可以解决这个问题,待实验。
2 、种类重复问题,这个问题如果要解决,那么在每个金额处至少要多一个n方的时间,还要开一个很大的数组来暴力解决问题。比如币值为1、2,2的组合有1、1, 0、2,于是4的计算结果为4,实际结果为3,为什么? 因为组合0 1 1 2我计算了两遍而实际上只算一遍。
代码:
#include<stdio.h> #include<stdlib.h> #include<string.h>
int opt[10001],w[25];
void init() { memset(opt, 0, sizeof(opt)); opt[0] = 1; }
int main() { int v,n; scanf("%d%d",&v,&n);
init();
for(int i=0; i<v; i++) { scanf("%d",&w[i]); }
for(int i=0; i<v; i++)
for(int j=w[i]; j<=n; j++)
{ if(opt[j-w[i]]>0)
{ opt[j] += opt[j-w[i]]; }
}
printf("%d\n",opt[n]);
}
代码实现真的很简单,但这道题我还是做了接近2小时。。