【日常刷題】郵票面值設計(搜索與動態規劃)

郵票面值設計

對於這一道題目,我們可以採取搜索的手段去枚舉每一張郵票,再用動態規劃求得最大的連續數值。其中的難點就是如何進行枚舉,至於動態規劃則僅僅是完全揹包的一個簡單變形。

搜索:讓我們最後輸出的總數中要求從小到大按照次序輸入,那麼我們從小到大枚舉即可。我們可以知道,數字1是必須要去的,不然就無法枚舉到了數字1;接着,我們就要去枚舉了。我們使用遞歸的參數來記錄取的個數take,最大連續和MAX,總和sum。那麼新取的數的範圍就必然在temp[take]+1~MAX+1(temp記錄取的數字)之間,做區間是爲了保證有序性,右區間則保證了不會超過MAX+1,不然就無法枚舉到這個MAX+1這個值了。枚舉完之後,我們就考慮如何得到這個連續值。

DP動態規劃:完全揹包的簡單變形。設f[i]爲組成數字i的最小郵票數.f[j]=min(f[j],f[jtemp[i]]+1)f[j]=min(f[j],f[j-temp[i]]+1)即數字爲j的,取了數字temp[i]就在原來的基礎上面+1的情況。但是需要注意,枚舉數字的最大範圍是sum×n,表示每一件物品都取上限n個。最後的取值最大連續的數字答案ans的表達式爲:ans=max(i),(f[1]f[i]nans=max(i),(f[1]-f[i]≤n)
只要線性掃描這個答案區間即可。

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