【日常刷题】邮票面值设计(搜索与动态规划)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/83146639

邮票面值设计

对于这一道题目,我们可以采取搜索的手段去枚举每一张邮票,再用动态规划求得最大的连续数值。其中的难点就是如何进行枚举,至于动态规划则仅仅是完全背包的一个简单变形。

搜索:让我们最后输出的总数中要求从小到大按照次序输入,那么我们从小到大枚举即可。我们可以知道,数字1是必须要去的,不然就无法枚举到了数字1;接着,我们就要去枚举了。我们使用递归的参数来记录取的个数take,最大连续和MAX,总和sum。那么新取的数的范围就必然在temp[take]+1~MAX+1(temp记录取的数字)之间,做区间是为了保证有序性,右区间则保证了不会超过MAX+1,不然就无法枚举到这个MAX+1这个值了。枚举完之后,我们就考虑如何得到这个连续值。

DP动态规划:完全背包的简单变形。设f[i]为组成数字i的最小邮票数. f [ j ] = m i n ( f [ j ] , f [ j t e m p [ i ] ] + 1 ) f[j]=min(f[j],f[j-temp[i]]+1) 即数字为j的,取了数字temp[i]就在原来的基础上面+1的情况。但是需要注意,枚举数字的最大范围是sum×n,表示每一件物品都取上限n个。最后的取值最大连续的数字答案ans的表达式为: a n s = m a x ( i ) , ( f [ 1 ] f [ i ] n ans=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;
}

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/83146639