儘管做完蒙德里安的夢想以後我就沒有夢想了,但是還是要繼續的。這不是又遇到一個題很有意思 ,沒有想到的我哭了,就是裸題,我還是太菜了。
取硬幣
題目描述:
現在有 n1+n2 種面值的硬幣,其中前 n1 種爲普通幣,可以取任意枚,後 n2 種爲紀念幣,每種最多隻能取 1 枚,每種硬幣有一個面值,問能用多少種方法拼出 m 的面值?
輸入格式
第一行包含三個整數 n1,n2,m,分別表示普通幣種類數,紀念幣種類數和目標面值;
第二行 n1 個整數,第 i 種普通幣的面值 a[i]。保證 a[i] 爲嚴格升序;
第三行 n2 個整數,第 i 中紀念幣的面試 b[i]。保證 b[i] 爲嚴格升序。
輸出格式
共一行,包含一個整數 x,表示方法總數對 109+7 取模後的結果。
注意,不要忘記取模。
數據範圍
對於 30% 的數據,保證 1≤n1+n2≤10,1≤m≤100,1≤a[i]≤100,1≤b[i]≤100。
對於 100% 的數據,保證 1≤n1+n2≤100,1≤m≤100000,1≤a[i]≤100000,1≤b[i]≤100000。
輸入樣例:
3 1 5
1 2 3
1
輸出樣例:
9
樣例解釋
(x) 代表面值爲x的普通幣,[x]代表面值爲x的紀念幣,樣例所有方法數如下:
(1)(1)(1)(1)(1)
(1)(1)(1)(2)
(1)(1)(3)
(1)(2)(2)
(2)(3)
(1)(1)(1)(1)[1]
(1)(1)[1](2)
(1)[1](3)
[1](2)(2)
算法思路:完全揹包加01揹包 hhh
揹包問題:完全揹包(普通幣) + 0/1揹包(紀念幣)
時間複雜度: O(nm)
f[ i ][ j ]:1.只從前i種硬幣中選,且總面值是j的選法的集合 2.數量
集合可以分爲兩種:不選硬幣i + 選硬幣i
不選硬幣i: f[i][j] = f[i-1][j]
選硬幣i後:1. 紀念幣(0/1揹包)f[i][j] = f[i-1][j] + f[i-1][j-p[i]]
選硬幣i後:2. 普通幣種(完全揹包)f[i][j] = f[i-1][j] + f[i-1][j-p[i]] + f[i-1][j-2p[i]] + …
優化:f[i][j-p[i]] = f[i-1][j-p[i]] + f[i-1][j-2p[i]] + f[i-1][j-3p[ i ] ] + …
f[i][j] = f[i-1][j] + f[ i][j-p[i] ]
二維優化成一維
完整代碼(已AC):
#include <iostream>
using namespace std;
const int N = 100010;
const int MOD = 1e9 + 7;
int n1 = 0;
int n2 = 0;
int m = 0;
int f[N];
int main()
{
// f[i][j] = f[i-1][j] + f[i-1][j-p[i]]; 01揹包
// f[i][j] = f[i - 1][j] + f[i][j-p[i]]; 完全揹包
cin >> n1 >> n2 >> m;
f[0] = 1;
for (int i = 1; i <= n1; i++)
{
int p = 0;
cin >> p;
for (int j = p; j <= m; j++)
{
f[j] = (f[j] + f[j - p]) % MOD;
}
}
for (int i = 1; i <= n2; i++)
{
int p = 0;
cin >> p;
for (int j = m; j >= p; j--)
{
f[j] = (f[j] + f[j - p]) % MOD;
}
}
cout << f[m] << endl;
return 0;
}