A1123. 邮票面值设计
时间限制:
1.0s 内存限制:
256.0MB
试题来源
NOIP1999 提高组
问题描述
给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤13)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1~MAX之间的每一个邮资值都能得到。
例如,N=3,K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大值,所以MAX=7,面值分别为1分、3分。
例如,N=3,K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大值,所以MAX=7,面值分别为1分、3分。
输入格式
一行,两个数N、K
输出格式
两行,第一行升序输出设计的邮票面值,第二行输出“MAX=xx”(不含引号),其中xx为所求的能得到的连续邮资最大值。
样例输入
3 2
样例输出
1 3
MAX=7
MAX=7
解析:
①搜索。对每一步,枚举邮票面值,然后搜索下一张邮票面值并更新最优解。
②完全背包确定搜索范围。
假设现在枚举到第 i 张邮票面值,第 i-1 张邮票面值为a[i-1],前 i-1 张邮票得到的最大连续值为x,则第 i 张邮票面值的范围就为 [a[i-1]+1,x+1];
假设现在有 n 张邮票,怎么得到其最大连续值呢?
用 f[i] 记录达到数值 i 所需的最小邮票数量,初始化为一个极大值。然后用完全背包算出 f[i] 的值,从 0 开始,第一个f[i]>n,则 i-1 就为最大连续值。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn1=40;
const int maxn2=10000;
int n,m,ans=0,f[maxn1][maxn2];
int a[maxn1+20],b[maxn2+20];
void dfs(int step,int s,int e)
{
int i,j,k;
if(step>m)
{
if(ans<e-1)for(ans=e-1,i=1;i<=m;i++)b[i]=a[i];
return;
}
for(k=e;k>=s;k--)
{
j=a[step-1]*n;
for(i=0;i<=j;i++)f[step][i]=f[step-1][i];
memset(&f[step][j+1],25,sizeof(int)*((n*k+1-j)+10));
for(j=n*k,i=k;i<=j;i++)
f[step][i]=min(f[step][i],f[step][i-k]+1);
for(i=e;i<=j+1;i++)if(f[step][i]>n)
{
a[step]=k,dfs(step+1,k+1,i);
break;
}
}
}
int main()
{
int i;
memset(f[1],25,sizeof(f[1]));
scanf("%d%d",&n,&m);
for(i=0;i<=n;i++)f[1][i]=i;
a[1]=1,dfs(2,2,n+1);
for(i=1;i<m;i++)printf("%d ",b[i]);
printf("%d\nMAX=%d\n",b[m],ans);
return 0;
}