零钱组合
給定面值為1,5,10,20,50的零錢,給定一個金額A,求組合成A的方法的數量
假定一個面額為x,則組合為A的方法數目 = 不使用x面額的零錢組合成A的方法的數量 + 使用任何面值的零錢組合成A -x的方法的數量
可以看成兩類,一類是沒有面額x而組合成A的數量,另一類是至少用一張面值為x而組合成A的方法的數量
直接按照算法的描述,遞推關系應該是:
nway[v][n]=nway[v-1][n]+nway[v][n-coin[v]];
面額存儲為coin= {1,5,10,20,50},下標為v,給定的金額為n
使用任何0-v面額的零錢,組合成n的方法數量 = 使用任何0-(v-1)面額的零錢組合成n的方法數量 + 使用任何0-v面額零錢組合成n-coin[v]的數量
動態規劃的遞推式
for(i=0;i<=N;++i)
nway[0][i] = 1;//只用一種面額的零錢,對于任何N,方法都只有一種
for(i=0;i<5;++i)
nway[i][0] = 1;//如果N=0,方法也只有一種,就是不出任何零錢
for(v=1;v<5;++v)
{
for(n = coin[v];n<=N;++n)
{
nway[v][n]=nway[v-1][n]+nway[v][n-coin[v]];
}
}
由于nway[v-1][n]只會用一次,所以可以合并nway[v][n]=nway[v-1][n]
nway[0] = 1
for(v=0;v<5;++v)
for(n = coin[v];n<=N;++n)
nway[n] +=nway[n-coin[v]];//(可以擴展為nway[n] = nway[n] + nway[n-coin[v]])其中第二個nway[n]可以看做是當v-1時的方法數目
由題目可知,coin可以是任何順序,結果不變
//
上面的思路沒錯,但是程序寫錯了
解法1:可以出來金額為12,16這樣的不能完全湊整的
const int N = 45;//錢總數 const int K = 2;//錢的種類 int main() {int nway[K][N+1];memset(nway,0,sizeof(nway));int coin[]= {5,10};int i;for(i=coin[0];i<=N;++i)nway[0][i] = 1;//只用一種面額的零錢,對于任何N,方法都只有一種for(i=0;i<K;++i)nway[i][0] = 1;//如果N=0,方法也只有一種,就是不出任何零錢for(int v=1;v<K;++v){for(n = coin[v];n<=N;++n){nway[v][n]=nway[v-1][n]+(nway[v][n-coin[v]]?nway[v][n-coin[v]]:1);}}//cout<<nway[1][11]<<endl;cout<<nway[K-1][N]<<endl;return 0;}
解法2:貌似這個更容易理解
int nway[N+1];memset(nway,0,sizeof(nway));int coin[]= {5,10,20};nway[0] = 1;for (int i=0;i<K;++i){for (int v =coin[i];v<=N;++v){nway[v] = nway[v] + (nway[v-coin[i]]?nway[v-coin[i]]:1);//nway[v]是指在i-1時的方案數量,表示完全沒有coin[i]的方案數量;而在v之前的下標均表示在i時的方案數量,也就是說至少有一個coin[i]的方案的數量,但是如果nway[v-coin[i]]=0,此時就要加1}}cout<<nway[N]<<endl;
上面的算法有瑕疵,就是如果輸入43
,按照題目的意思應該得0,但是上面的程序會輸出一個40的所有組合
int nway[N+1];memset(nway,0,sizeof(nway));int coin[]= {5,10,20};nway[0] = 1;for (int i=0;i<K;++i){for (int v =coin[i];v<=N;++v){nway[v] = nway[v] + nway[v-coin[i]];}}cout<<nway[N]<<endl;再重新解釋下程序,nway[v]是在0到i-1種貨幣下,組合成v面額的數量;那在0到i種貨幣下,組合成面額v的方案的數量為=不含有第i種貨幣的組合方案數量+至少含有一張第i種貨幣的組合方案數量其中nway[v-coin[i]]
表示0到i種貨幣組成面額為v-coin[i]的方案的數量,可能含有i貨幣也可能不含有,但是我們預留一個coin[i]的位置,也就使得至少有一張i貨幣;
經過多方論證,用一維nway正好,如果用二維的,就回出錯。
在網上看到另外一種算法:
http://www.cnblogs.com/buptLizer/archive/2012/03/31/2427583.html
求用1,2,5這三個數不同個數組合的和為100的組合個數
?假設a*1 + b*2 + c*5 = 100;非常容易看出c的循環次數最小,那么枚舉c開始
c=0, a的取值為100,98,96,94,...,4,2,0
c=1,a的取值為95,93,91,89,...,5,3,1
c=2,a的取值為90,88,86,...,4,2,1
c=3,a的取值為85,83,81,...,5,3,1
.....
c=19,a的取值為5,3,1
c=20,a的取值0
現在看出規律了吧,100以內的偶數+95以內的奇數+90以內的偶數+。。。+5以內的奇數+0以內的偶數
我們可以立即寫好如下程序:
for(int i = 0; i <= 100; i += 5)
{
??? sum += (m>>1) + 1;
}
總結
- 上一篇: static_const和reinter
- 下一篇: 背包问题代码