版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/83146639
邮票面值设计
对于这一道题目,我们可以采取搜索的手段去枚举每一张邮票,再用动态规划求得最大的连续数值。其中的难点就是如何进行枚举,至于动态规划则仅仅是完全背包的一个简单变形。
搜索:让我们最后输出的总数中要求从小到大按照次序输入,那么我们从小到大枚举即可。我们可以知道,数字1是必须要去的,不然就无法枚举到了数字1;接着,我们就要去枚举了。我们使用递归的参数来记录取的个数take,最大连续和MAX,总和sum。那么新取的数的范围就必然在temp[take]+1~MAX+1(temp记录取的数字)之间,做区间是为了保证有序性,右区间则保证了不会超过MAX+1,不然就无法枚举到这个MAX+1这个值了。枚举完之后,我们就考虑如何得到这个连续值。
DP动态规划:完全背包的简单变形。设f[i]为组成数字i的最小邮票数.
即数字为j的,取了数字temp[i]就在原来的基础上面+1的情况。但是需要注意,枚举数字的最大范围是sum×n,表示每一件物品都取上限n个。最后的取值最大连续的数字答案ans的表达式为:
只要线性扫描这个答案区间即可。
CODE
#include<bits/stdc++.h>
using namespace std;
#define MAXN 500
#define MAXNN 2000
#define INT register int
int n,k,ans=0;
int f[MAXNN],temp[MAXN],fin[MAXN];
inline int read()
{
int s=0,w=1;char c=getchar();
while (c<'0' || c>'9') c=getchar();
while (c>='0' && c<='9') {s=s*10+c-48; c=getchar();}
return s*w;
}
int DP(int N,int sum)
{
memset(f,127,sizeof(f));
f[0]=0;
INT i,j;
for (i=1;i<=N;++i)
for (j=temp[i];j<=sum*n;++j)
f[j]=min(f[j],f[j-temp[i]]+1);
i=1;
for (i=1;i<=sum*n;++i) if (f[i]>n) return i-1;
return n*sum;
}
void dfs(int take,int MAX,int sum)
{
if (take==k)
{
if (MAX>ans)
{
ans=MAX;
for (int i=1;i<=k;++i) fin[i]=temp[i];
}
return;
}
INT i;
for (i=temp[take]+1;i<=MAX+1;++i)
{
temp[take+1]=i;
int MAXnum=DP(take+1,sum+i);
dfs(take+1,MAXnum,sum+i);
}
return;
}
int main()
{
n=read();k=read();
temp[1]=1;
dfs(1,n,1);
for (int i=1;i<k;++i) printf("%d ",fin[i]);
printf("%d\nMAX=%d\n",fin[k],ans);
return 0;
}