• USACO / Stamps(DP)


    描述

    已知一个 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 2
    1 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_3531
    LANG: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;
    }
    
  • 相关阅读:
    httpmime-session 会话保持
    5.5 准备创建bean
    5.4 获取单例
    当有“Button1.Attributes.Add("onclick", "return confirm('你确定要保存修改吗?')");”时,验证控件失效的解决方法
    asp.net刷新本页面的六种方法总结
    return 、break和continue的区别和作用
    OnClientClick事件
    抽象接口
    C# 中的EventHandler实例详解-转
    GridView点击行触发SelectedIndexChanged事件
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/2598262.html
Copyright © 2020-2023  润新知