zoukankan      html  css  js  c++  java
  • [Luogu] P1899 魔法物品

    Luogu P1899 魔法物品


    题目描述:

    有两种类型的物品:普通物品和魔法物品。普通物品没有魔法属性,而魔法物品拥有一些魔法属性。每种普通物品有一个价值(P),但每种魔法物品有两种价值:鉴定前的价值(P_{1})和鉴定后的价值(P_{2})(当然,(P_{1})总是大于(P_{2}))。

    为了鉴定一个魔法物品,你需要购买一个魔法卷轴,用它来鉴定魔法物品。鉴定完一件魔法物品之后,鉴定卷轴就会消失。每个鉴定卷轴需要(S)元钱,如果没有足够的钱,你将无法购买任何魔法卷轴。

    现在,你正在一个集市中,同时拥有很多物品。你知道每件物品的价值并且想要出售全部物品。那么,你最多可以获得多少钱呢?

    你可以假定:

    • 开始的时候你没有钱。
    • 所有的魔法物品还没有被鉴定。
    • 只要你有足够的钱,你可以购买任意多的魔法卷轴。

    输入输出格式:

    输入格式(magic.in):

    第一行有两个整数(N)(S)((0 < S leqslant 10000)),表示你拥有的物品数和一个鉴定卷轴价格。

    接下来(N)行,每行给出一件物品的价格。

    对于每件普通物品,那一行仅有一个整数(P)((0 < P leqslant 10000))。

    对于每件普通物品,那一行将会有两个整数(P_{1})(P_{2})((0 < P_{1} < P_{2} leqslant 10000))。

    输出格式(magic.out):

    一个整数表示你最多能够获得多少钱。

    样例:

    输入#1:

    2 10
    10
    20 100
    

    输出#1:

    100
    

    数据规模:

    对于30%的数据,(N leqslant 50)

    对于100%的数据,(N leqslant 1000)

    传送门

    题解:

    首先考虑输入时的处理。显然在处理普通物品的时候,可以直接选择把他们卖掉,作为买卷轴的本钱。其次考虑魔法物品。当魔法物品的(P_{2})减掉(S)不大于(P_{1})时,把它卖掉也是一定一的可以的。由此,我们将只把(P_{2} - S geqslant P_{1})的魔法物品存下来。

    其次进行对魔法物品的处理。(为方便表示,设(ans)为卖掉所有可卖物品后手中的金钱,(P_{i,1}, P_{i, 2})分别代表第(i)个待处理魔法物品的(P_{1}, P_{2})(tot)为所有物品的(P_{1}))。

    1. 首先是当(ans geqslant S)时。因为所有待处理的物品(i)都满足(P_{i, 2} - S geqslant P_{i, 1}),所以,先买到一个魔法卷轴、鉴定并且出售第一个魔法物品后,(ans)的值一定比买卷轴之前大。所以直接把所有未处理的魔法物品进行鉴定,然后按魔法价格售出即可。
    2. 其次是当(ans < S)时。我们需要售出一些魔法物品使(ans geqslant S),之后即可按上一种情况处理。这个时候就需要跑个DP来计算卖哪个(哪些)比较优。可得(P_{i, 2} - S - P_{i, 1})为第(i)个物品的损失,转移方程:(F[j] = min(F[j], f[j - P_{i, 1}] + (P_{i, 2} - S - P_{i, 1})))(其中(F[j])为不算(ans)在内,再得到(j)元时所产生的最小损失和)。跑一遍DP计算最小的损失(设为(minn))((S - ans leqslant j leqslant tot))。如果能够凑出使(ans geqslant S)的金钱的话,则按情况一处理,然后(ans - minn)即可;如果不能,意味着一个魔法卷轴都买不到,直接输出(tot)即可。

    另:如果单纯按情况二写的话,时间复杂度会出锅((N * tot leqslant 1e^{10}));所以需要将DP稍微优化一下:令(F[j])代表当所得金钱(geqslant j) 时最小的代价和。转移方程:(F[j] = min(f[j], f[max(j - P_{i, 1}, 0)] +(P_{i, 2} - S - P_{i, 1})))。((N * S leqslant 5e^{6})

    代码:

    #include <cstdio>
    const int INF = 999999999;
    const int MAXP = 5000;
    int n, s, top, ans, tmp, tmp2, tmp3, tot, xxx;
    int p[1010], o[1010], f[5001];
    char c(0);
    inline int min(int a, int b) {
        return (a < b ? a : b);
    }
    inline int max(int a, int b) {
        return (a > b ? a : b);
    }
    int main() {
        scanf("%d%d", &n, &s);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &tmp);
            tot = tot + tmp;
            scanf("%c", &c);
            if(c == ' ') {
                scanf("%d", &tmp2);
                if(tmp2 - s > tmp) {
                    p[++top] = tmp2;
                    o[top] = tmp;
                }
                else ans = ans + tmp;
            } else {ans = ans + tmp;}
        }
        xxx = ans;							//存储ans的值,因为下一步要对ans进行操作
        for (int i = 1; i <= top; ++i)		//按情况一处理
            ans = ans + p[i] - s; 
        if(xxx < s){						//如果为情况二
            tmp3 = min(MAXP, tot);	//因为f[j]代表了大于等于j的最小值,所以DP跑到卷轴价格的最大值5000即可
            for (int i = 1; i <= tmp3; ++i) f[i] = INF;
            for (int i = 1; i <= top; ++i) {
                tmp = o[i]; tmp2 = p[i] - s - tmp; 
                for (int j = tmp3; j >= 1; j--)
                    f[j] = min(f[j], f[max(j - tmp, 0)] + tmp2);	//取max(j-tmp,0)以免得负值
                }
            int minn(INF);
            for (int i = s - xxx; i <= tmp3; ++i)			//取最小代价和
                minn = min(minn, f[i]);
            if(minn < INF) printf("%d
    ", ans - minn);		//
            else printf("%d
    ", tot);
        } else {printf("%d
    ", ans); return 0;}
        return 0;
    }
    
  • 相关阅读:
    poj 1328 Radar Installation (贪心)
    hdu 2037 今年暑假不AC (贪心)
    poj 2965 The Pilots Brothers' refrigerator (dfs)
    poj 1753 Flip Game (dfs)
    hdu 2838 Cow Sorting (树状数组)
    hdu 1058 Humble Numbers (DP)
    hdu 1069 Monkey and Banana (DP)
    hdu 1087 Super Jumping! Jumping! Jumping! (DP)
    必须知道的.NET FrameWork
    使用记事本+CSC编译程序
  • 原文地址:https://www.cnblogs.com/manziqi/p/8964296.html
Copyright © 2011-2022 走看看