题目:硬币问题。有n种硬币,面值分别为V1, V2, …, Vn,每种都有无限多。给定非负整数S, 可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值。 1≤n≤100,0≤S≤10000,1≤Vi≤S。
分析:这个模型和上一题类似,但也有一些明显的不同之处:上题并没有确定路径的起点和终点(可以把任意矩形放在第一个和最后一个),而本题的起点必须为S,终点必须为0;点固 定之后“最短路”才是有意义的。
思路:类似完全背包。输出方案用了两种方法。
//记录路径
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int v[105];
int s, n;
int MAXdp[105];
int MINdp[105];
int L_max[105], L_min[105];
int main()
{
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
}
MAXdp[0]=MINdp[0]=0;
for(int i=1;i<=s;i++)
{
MAXdp[i]=0;
MINdp[i]=(1<<31)-1;
}
for(int i=1;i<=s;i++)
{
for(int j=1;j<=n;j++)
{
if(i>=v[j])
{
if(MAXdp[i]<MAXdp[i-v[j]]+1)
{
MAXdp[i]=MAXdp[i-v[j]]+1;
L_max[i]=j;//得到i的上一步选择,记录路径
}
if(MINdp[i]>MINdp[i-v[j]]+1)
{
MINdp[i]=MINdp[i-v[j]]+1;
L_min[i]=j;//得到i的上一步选择,记录路径
}
}
}
}
printf("%d %d\n",MAXdp[s],MINdp[s]);
for(int i=s;i;)
{
printf("%d ",L_max[i]);
i-=v[L_max[i]];
}
printf("\n");
for(int i=s;i;)
{
printf("%d ",L_min[i]);
i-=v[L_min[i]];
}
printf("\n");
return 0;
}
turn 0;
}
//回溯路径
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int v[105];
int s, n;
int MAXdp[105];
int MINdp[105];
int L_max[105], L_min[105];
int print_ans(int *d, int s)//回溯
{
if(s==0)
{
return 0;
}
for(int i=1;i<=n;i++)
{
if(s>=v[i]&&d[s]==d[s-v[i]]+1)
{
printf("%d ",i);
print_ans(d, s-v[i]);
break;
}
}
}
int main()
{
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
}
MAXdp[0]=MINdp[0]=0;
for(int i=1;i<=s;i++)
{
MAXdp[i]=0;
MINdp[i]=(1<<31)-1;
}
for(int i=1;i<=s;i++)
{
for(int j=1;j<=n;j++)
{
if(i>=v[j])
{
MAXdp[i]=max(MAXdp[i], MAXdp[i-v[j]]+1);
MINdp[i]=min(MINdp[i], MINdp[i-v[j]]+1);
}
}
}
printf("%d %d\n",MAXdp[s],MINdp[s]);
print_ans(MAXdp, s);
printf("\n");
print_ans(MINdp, s);
printf("\n");
return 0;
}