题目:有n种硬币,面值分别为V1,V2,...Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值!
分析:我们把每种面值看作一个点!表示“还需要凑足的面值”,初始状态为S,目标状态为0。那么若当前状态在i,每使用一个硬币j,状态便转移到i-Vj。
代码:
记忆化搜索:
#include <iostream>
#include <string>
using namespace std;
int n, S, V[MAXN], d[MAXN], vis[MAXN];
const int MAXN = 10000;
const int INF = 100000000;
int dpmax(int S) {
if(vis[S]) return d[S];
vis[S] = 1;
int &ans = d[S];
ans = -1 << 30;
for(int i = 1; i <= n; ++i) {
if(S >= V[i]) ans = max(ans, dpmax(S - V[i]) + 1);
}
return ans;
}
int dpmin(int S) {
if(vis[S]) return d[S];
vis[S] = 1;
int &ans = d[S];
ans = -1 >> 30;
for(int i = 1; i <= n; ++i) {
if(S >= V[i]) ans = min(ans, dpmin(S - V[i]) + 1);
}
return ans;
}
int main() {
memset(vis, 0, sizeof(vis));
cin >> n >> S;
for(int i = 1; i <= n; ++i) {
cin >> V[i];
}
cout << dpmax(S) << endl;
cout << dpmin(S) << endl;
return 0;
}
递推:
打印字典序最小的路径:
<pre class="cpp" name="code">#include <iostream>
#include <string>
using namespace std;
const int MAXN = 10000;
const int INF = 1000000000;
int n, S, V[MAXN], minn[MAXN], maxn[MAXN]; //minn[i]表示还需凑足价值为i的话,所需的最少的硬币数目!maxn[i]表示还需凑足价值为i的话,所需的最多的硬币数目!
void print_ans(int* d, int S) {
for(int i = 1; i <= n; ++i) {
if(S >= V[i] && d[S] == d[S - V[i]] + 1){ //从小到大寻找满足条件的那个点!
cout << i << " ";
print_ans(d, S-V[i]);
break; //这个不能忘记!
}
}
}
int main() {
cin >> n >> S;
for(int i = 1; i <= n; ++i) {
cin >> V[i];
}
memset(minn, INF, sizeof(minn));
memset(maxn, -INF, sizeof(maxn));
for(int i = 1; i <= S; ++i) { //递推!求出所有minn[1...S]与maxn[1...S]!
for(int j = 1; j <= n; ++j) {
if(i >= V[j]) {
minn[i] = min(minn[i], minn[i - V[j]] + 1);
maxn[i] = max(maxn[i], maxn[i - V[j]] + 1);
}
}
}
cout << minn[S] << endl;
cout << maxn[S] << endl;
print_ans(minn, S);
cout << endl;
print_ans(maxn, S);
cout << endl;
return 0;
}
另外一种打印路径的方法:
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 10000;
const int INF = 1000000000;
int n, S, V[MAXN], minn[MAXN], maxn[MAXN]; //minn[i]表示还需凑足价值为i的话,所需的最少的硬币数目!maxn[i]表示还需凑足价值为i的话,所需的最多的硬币数目!
int min_coin[MAXN], max_coin[MAXN]; //min_coin[S]记录的是满足minn[S] = minn[S-V[i]]+1的最小的i。
void print_ans(int* d, int S) {
while(S) {
cout << d[S] << " ";
S -= V[d[S]];
}
}
int main() {
cin >> n >> S;
for(int i = 1; i <= n; ++i) {
cin >> V[i];
}
memset(minn, INF, sizeof(minn));
memset(maxn, -INF, sizeof(maxn));
for(int i = 1; i <= S; ++i) { //递推!求出所有minn[1...S]与maxn[1...S]!
for(int j = 1; j <= n; ++j) {
if(i >= V[j]) {
if(minn[i] > minn[i - V[j]] + 1) {
minn[i] = minn[i - V[j]] + 1;
min_coin[i] = j;
}
if(maxn[i] < maxn[i - V[j]] + 1) {
maxn[i] = maxn[i - V[j]] + 1;
max_coin[i] = j;
}
}
}
}
cout << minn[S] << endl;
cout << maxn[S] << endl;
print_ans(min_coin, S);
cout << endl;
print_ans(max_coin, S);
cout << endl;
return 0;
}