代码改变世界

动态规划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小时。。