【NOIP1999】邮票面值设计(水题水积分)



题目描述

给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤40)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1~MAX之间的每一个邮资值都能得到。

输入

第1行:2个整数N K

输出

第一行从小到大写出所用邮票面值,

第二行写"MAX="MAX

样例输入

3 2

样例输出

1 3

MAX=7
分析

     这是一道多年前的NOIP题,数据比较小,现在比较容易做,但是数据变大之后暴力就没办法做了,需要找到一个比较普遍的做法。观察题目,感觉没有什么特别好的方法,想到用DFS,搜索n张k种邮票所能够连续达到的最大值。根据题意,面值为1分的邮票一定会被选中,所以从第2张邮票的面值开始枚举。分析可知,第k张邮票的面值不会超过使用n张前k-1种邮票所能达到的最大值+1,确定了枚举的上界;而如果第k种邮票的面值<第k-1种,那么所能达到的当前最大值一定不会是最优的(第k-1种邮票就已经能够达到)。在寻找当前所能达到的最大值时使用揹包的思路,这样就能较高效地解决这道题。

代码如下:

#include<cstdio>
#define MAXN 40+5
#define INF (1<<29)
using namespace std;
int n,k;
int max(int a,int b)
{
    returna>b?a:b;
}
int val[MAXN],maxv[MAXN],op[MAXN],f[100*MAXN];//val[i]是第i种邮票的面值,maxv[i]是在前i种邮票最多n张的情况下所能连续达到的最大值,op保存了最后的输出结果
int maxn;
void dp(int s)
{
    intj,l=s-1;
    while(f[l]<=n)
    {
        f[++l]=INF;
        for(j=1;j<=s&&l>=val[j];j++)//由于之前已经做过之前的揹包,并且回溯后做过的揹包也不会对结果产生影响,可以优化节省时间
            if(f[l]>f[l-val[j]]+1)
                f[l]=f[l-val[j]]+1;
    }
    if(l-1>maxv[s])
        maxv[s]=l-1;
}
void dfs(int s)
{
    intv;
    if(s==k+1)
    {
        if(maxv[s-1]>maxn)
        {
            maxn=maxv[s-1];
            for(v=1;v<=k;v++)
                op[v]=val[v];
        }
        return;
    }
    for(v=maxv[s-1]+1;v>val[s-1];v--)
    {
        val[s]=v;
        dp(s);
        dfs(s+1);
    }
}
int main()
{
    inti;
    scanf("%d%d",&n,&k);
    val[1]=1,maxv[1]=n;
    for(i=1;i<=n;i++)
        f[i]=i;
    dfs(2);
    printf("%d",op[1]);
    for(i=2;i<=k;i++)
        printf(" %d",op[i]);
    printf("\nMAX=%d",maxn);
}



发布了34 篇原创文章 · 获赞 4 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章