郵票面值設計
對於這一道題目,我們可以採取搜索的手段去枚舉每一張郵票,再用動態規劃求得最大的連續數值。其中的難點就是如何進行枚舉,至於動態規劃則僅僅是完全揹包的一個簡單變形。
搜索:讓我們最後輸出的總數中要求從小到大按照次序輸入,那麼我們從小到大枚舉即可。我們可以知道,數字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;
}