USACO / Stamps(DP)

来源:互联网 发布:如何在淘宝上投诉卖家 编辑:程序博客网 时间:2024/05/16 17:07

描述

已知一个 N 枚邮票的面值集合(如,{1 分,3 分})和一个上限 K —— 表示信封上能够贴 K 张邮票。计算从 1 到 M 的最大连续可贴出的邮资。

例如,假设有 1 分和 3 分的邮票;你最多可以贴 5 张邮票。很容易贴出 1 到 5 分的邮资(用 1 分邮票贴就行了),接下来的邮资也不难:

6 = 3 + 3 7 = 3 + 3 + 1 8 = 3 + 3 + 1 + 1 9 = 3 + 3 + 3 10 = 3 + 3 + 3 + 1 11 = 3 + 3 + 3 + 1 + 1 12 = 3 + 3 + 3 + 3 13 = 3 + 3 + 3 + 3 + 1。 

然而,使用 5 枚 1 分或者 3 分的邮票根本不可能贴出 14 分的邮资。因此,对于这两种邮票的集合和上限 K=5,答案是 M=13。

[规模最大的一个点的时限是3s]

格式

PROGRAM NAME: stamps

INPUT FORMAT:

(file stamps.in)

第 1 行: 两个整数,K 和 N。K(1 <= K <= 200)是可用的邮票总数。N(1 <= N <= 50)是邮票面值的数量。

第 2 行 .. 文件末: N 个整数,每行 15 个,列出所有的 N 个邮票的面值,每张邮票的面值不超过 10000。

OUTPUT FORMAT:

(file stamps.out)

第 1 行:一个整数,从 1 分开始连续的可用集合中不多于 K 张邮票贴出的邮资数。

SAMPLE INPUT

5 21 3

SAMPLE OUTPUT 

13
分析:
简单DP题。水题浪费时间还是太多。。。
先说下思路:首先想到设b[i]表示i可不可以被表示,然后b[i]=b[i-a[0]] || b[i-a[1]] || ......b[i-a[j]]算出每个i,然后看最大连续的数到哪里。
后来发现这道题有个限制就是邮票最多只能用K张。所以简单换了下方程:设f[i]表示能贴出i需要的最少邮票数。b[i]的计算公式还是一样的,只不过要加上一个限制条件,f[i-a[j]]<K. ( j=0..N ) 但是这个方法有个缺陷,不知道把i算到哪里合适。当然这道题的最大可能是200*10000。
但马上就又可以想到一个更直接且更省空间的方法:1.根本不需要把b[i]记录下来。因为是求从1开始连续的,所以到我们算到的这个i为止,前面的数都是ok的。(即b[i]=1),所以算当前b[i]就可以利用前面的任何数。2.跳出条件,不需要非算到200*10000,一旦算的i不能贴出,则可以立刻跳出。并输出i-1。理由同上,因为题目是让求从1开始可以连续到最大的数。
代码:
/*ID:138_3531LANG:C++TASK:stamps*/#include <iostream>#include <fstream>#include <string>#include <cstring>#include <climits>using namespace std;int f[2001000];//能贴出i需要的最少邮票数int main(){    ifstream fin("stamps.in");    ofstream fout("stamps.out");    int i;    int K,N;    int ok;    int min;    int a[50];    memset(f,0,sizeof(f));    f[0]=1;    fin>>K>>N;    for (i=0;i<N;i++)        fin>>a[i];    for (i=1;;i++)    {        ok=0;        min=INT_MAX;        for (int j=0;j<N;j++)        {            if (i-a[j]>=0)                if ((f[i-a[j]]<K+1)&&(f[i-a[j]]>0))                {                    ok=1;                    if (f[i-a[j]]+1<min)                        min=f[i-a[j]]+1;                }        }        if (!ok)            break;        f[i]=min;    }    fout<<i-1<<endl;    return 0;}
原创粉丝点击