【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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章