题意:有n种硬币,面值分别为V1,V2,...Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬 币数目的最小值和最大值。
分析:我们把每种面值看作一个点!表示“还需要凑足的面值”,初始状态为S,目标状态为0。那么若当前状态在i,每使用一个硬币 j,状态便转移到i-Vj。
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10000;
const int INF = 0x3f3f3f3f;
int n, S, V[MAXN], d[MAXN], vis[MAXN];
int dpmax(int S)
{
if(vis[S]) return d[S];
vis[S] = 1;
int &ans = d[S];
ans = -INF;
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 = INF;
for(int i = 1; i <= n; ++i)
{
if(S >= V[i])
ans = min(ans, dpmin(S - V[i]) + 1);
}
return ans;
}
void print_ans(int s)
{
for(int i = 1; i <= n; i++)
{
if(s >= V[i] && d[s] == d[s - V[i]] + 1)
{
printf("%d ",V[i]);
print_ans(s-V[i]);
break;
}
}
}
int main()
{
memset(vis, 0, sizeof(vis));
cin >> n >> S;
for(int i = 1; i <= n; ++i)
cin >> V[i];
vis[0] = 1;
d[0] = 0;
cout << dpmax(S) << endl;
print_ans(S);
printf("\n");
memset(vis, 0, sizeof(vis));
vis[0] = 1;
d[0] = 0;
cout << dpmin(S)<< endl;
print_ans(S);
printf("\n");
return 0;
}