動態規劃 硬幣 POJ
總時間限制: 1000ms 記憶體限制: 262144kB
描述
宇航員Bob有一天來到火星上,他有收集硬幣的習慣。於是他將火星上所有面值的硬幣都收集起來了,一共有n種,每種只有一個:面值分別為a1,a2… an。 Bob在機場看到了一個特別喜歡的禮物,想買來送給朋友Alice,這個禮物的價格是X元。Bob很想知道為了買這個禮物他的哪些硬幣是必須被使用的,即Bob必須放棄收集好的哪些硬幣種類。飛機場不提供找零,只接受恰好X元。
輸入
第一行包含兩個正整數n和x。(1 <= n <= 200, 1 <= x <= 10000)
第二行從小到大為n個正整數a1, a2, a3 … an (1 <= ai <= x)
輸出
第一行是一個整數,即有多少種硬幣是必須被使用的。
第二行是這些必須使用的硬幣的面值(從小到大排列)。
樣例輸入
5 18
1 2 3 5 10
樣例輸出
2
5 10
本篇文章收錄了兩種不同的程式碼思路
分別來自@BeiYu-oi和@iamxym
說是兩種不同程式碼,其實整體的核心思路相同.不過是一個完全採用了動規的實現方法,另一個結合了遞迴;
核心思路:首先求出所有硬幣組合成總價值為j的方法總數(0 <= p <= x);然後考慮遞推關係式,遍歷每一個硬幣,如果硬幣必須被使用,則總價值為x的方法中,每一種都含有這個硬幣,若這個硬幣的價值為i,則總價值為x的組合方法中,不含有這個硬幣的方法數目為0;總價值為x - i的組合方法中,含有這個硬幣的方法數目也為0;這兩個判斷條件任選其一;
如何求出所有硬幣組合成總價值為i的方法總數呢?
開陣列kinds[10010],下標為總價值;
每一次輸入一個硬幣就更新一次kinds陣列,輸入硬幣後kinds[j] 有兩種選擇:不含有這個硬幣和含有;對於不含有的方法總數等於輸入硬幣前的kinds[j],含有這個硬幣大的方法數目等於輸入硬幣前的kinds[j - value](value為輸入硬幣的面值)
既然每次更新kinds[j]都要用到更新前的kinds[j - value],那就從後向前更新;
話不多說,上程式碼!
方法一:來自@BeiYu-oi
/** 程式名: 硬幣
* 作者: transient_arold
* 時間: 2017/5/31
*
*
*/ #include<iostream>
#include<cstring>
#include<algorithm> using namespace std; int kinds[10010]; // 儲存總價值為p的方法總數; int calc(int allvalue, int nowvalue) // 總價值為allvalue
//不含有這一枚面值為nowvalue的硬幣的方法數目;
{
if (allvalue < 0)
return 0;
else
return kinds[allvalue] - calc(allvalue - nowvalue, nowvalue);//總方法數減去含有這一枚面值為nowvalue的硬幣的方法數目;
//即減去總價值為allvalue - nowvalue,不含這枚硬幣的方法數目;
}
int main()
{
int n, x; // n是硬幣總數,x是要湊成的總面值;
cin >> n >> x;
int values[210]; // 儲存每一個硬幣的面值;
kinds[0] = 1;// 初始條件,總面值為0的方法為1; for (int i = 1; i <= n; ++i)
{
cin >> values[i]; /*每輸入一枚硬幣就更新一次kinds*/
for (int j = x; j >= values[i]; --j)
kinds[j] += kinds[j - values[i]];
} int finalcoin[210];
int cntcoin = 0; /*遍歷每一枚硬幣,
若總價值為x且不含這枚硬幣的方法數目為0,則必須含有這枚硬幣*/
for (int i = 1; i <= n; ++i)
if (calc(x, values[i]) == 0)
finalcoin[cntcoin++] = values[i];
cout << cntcoin << endl;
if (!cntcoin)
cout << endl;
else
for (int i = 0; i < cntcoin; ++i)
cout << finalcoin[i] << " ";
return 0;
} 方法二,來自@iamxym /*
* 硬幣 方法二
* 作者:hp
* 時間:2017/5/30 19:50:46
*
*/ #include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int kinds[10010]; // 總價值為j的硬幣組合種數
int mykinds[10010]; // 每輸入一次硬幣更新一次
// 表示不含這個硬幣,價值為j的硬幣組合種數目
//若mykinds[x]為0,則必須包含這一枚硬幣 int number[210];//必須包含的硬幣,其中num[0]表示必須包含的硬幣數目; int main()
{
int n, x;
cin >> n >> x;
int values[210]; // 表示每一個硬幣的面值
kinds[0] = 1; /*輸入硬幣並計算kinds[j]*/
for (int i = 1; i <= n; ++i)
{
cin >> values[i];
/*計算有i個硬幣時的kinds[j]*/
for (int j = x; j >= values[i]; --j)
kinds[j] += kinds[j - values[i]];
} /*逐個檢視每一個硬幣是不是必須的*/
for (int i = 1; i <= n; ++i)
{
memset(mykinds, 0, sizeof(mykinds));
mykinds[0] = 1;
for (int j = 1; j <= x; ++j)
if (j >= values[i])
mykinds[j] = kinds[j] - mykinds[j - values[i]];
else
mykinds[j] = kinds[j]; if (mykinds[x] == 0)
number[++number[0]] = values[i];
}
cout << number[0] << endl;
if (number[0] == 0)
cout << endl;
else {
sort(number + 1, number + number[0]);
for (int i = 1; i <= number[0]; i++)
cout << number[i] << " ";
}
return 0;
} 希望對大家有所幫助,也歡迎朋友們指出不足,提出問題,或給出更優演算法,謝謝!