杭电3466————DP之01背包(对状态转移方程的更新理解)

来源:互联网 发布:mac子弹头brave试色 编辑:程序博客网 时间:2024/06/06 12:40

Proud Merchants

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 2455    Accepted Submission(s): 1007


Problem Description
Recently, iSea went to an ancient country. For such a long time, it was the most wealthy and powerful kingdom in the world. As a result, the people in this country are still very proud even if their nation hasn’t been so wealthy any more.
The merchants were the most typical, each of them only sold exactly one item, the price was Pi, but they would refuse to make a trade with you if your money were less than Qi, and iSea evaluated every item a value Vi.
If he had M units of money, what’s the maximum value iSea could get?

 

Input
There are several test cases in the input.

Each test case begin with two integers N, M (1 ≤ N ≤ 500, 1 ≤ M ≤ 5000), indicating the items’ number and the initial money.
Then N lines follow, each line contains three numbers Pi, Qi and Vi (1 ≤ Pi ≤ Qi ≤ 100, 1 ≤ Vi ≤ 1000), their meaning is in the description.

The input terminates by end of file marker.

 

Output
For each test case, output one integer, indicating maximum value iSea could get.

 

Sample Input
2 1010 15 105 10 53 105 10 53 5 62 7 3
 

Sample Output
511
开始的时候没多想,以为这个题目很简单,于是写下了这样的代码
#include <cstdio>#include <algorithm>#include <cstring>#define maxn 5005using namespace std;struct Goods{    int value;    int price;    int minmoney;/*低于该价格不售出*/     /*且minmoney >= price*/ };/*商品:goods*/ int dp[maxn];/*dp[i]表示i元钱能购买的最大价值*/ struct Goods goods[5005];int cmp(struct Goods a,struct Goods b){    return a.minmoney  < b.minmoney ;}int main(){    int n,totalmoney;        while(scanf("%d%d",&n,&totalmoney) != EOF)    {        memset(dp,0,sizeof(dp));        memset(goods,0,sizeof(goods));                for(int i = 0 ; i < n ; i++)            scanf("%d%d%d",&goods[i].price,&goods[i].minmoney,&goods[i].value);        sort(goods,goods+n,cmp);        for(int i = 0 ; i < n ; i ++)            for(int j = totalmoney ; j >= goods[i].minmoney  ; j--)                    dp[j] = max(dp[j],dp[j-goods[i].price] + goods[i].value);        printf("%d\n",dp[totalmoney]);    }    return 0;}
样例测试一下没问题,但是不断WA,说实在的我不明白到底哪里错了。
思路很简单,想把goods[i]按照minmoney从小到大的顺序排列,尽量把minmoney小的先买了(放入背包)。
之后看了一下别人的题解,发现了自己逻辑上的错误。
比如说按照我的想法,举一个反例。
项目priceminmoneyvalue物品1 1020X物品299100Y    
按照之前的想法是先对物品3进行取舍。
如果先对物品1取舍  
物品2应当借助之前的状态(也就是购买了物品1或者不购买物品1之后的totalmoney的值)进行状态转移.但是如果物品2的minmoney过大的话,整个从goods[2].minmoney (即100) 到 totalmoney的这一区间内的所有的状态都没有了。但是如果先对物品2进行取舍的话,因为物品1的minmoney的值并不大,更有可能产生从goods[1].minmoney 到 totalmoney的状态.
从动规方程是可以看出,当前状态是由之前的状态决定的。所以之前状态的选择一到要有后效性(即能尽可能的影响到后边的状态)
事实上:
先选择物品1,能影响到的状态区间是[ goods[1].price + goods[2].minmoney , totalmoney ]
/*选择了物品1就花费了goods[1].price,然后你至少还需要goods[2].minmoney的钱才能选择物品2,这两者的和就是你想要尽可能多的影响后边的状态所需要的最少金钱*/
先选择物品2,能影响到的状态区间是[ goods[2].price + goods[1].minmoney , totalmoeny ]
/*选择了物品1就花费了goods[2].price,然后你至少还需要goods[1].minmoney的钱才能选择物品1,这两者的和就是你想要尽可能多的影响后边的状态所需要的最少金钱*/
到底先选择哪一个呢?我们想尽可能多的影响后边的状态,自然让区间的下限尽可能的小。
即:
假如先选择第i件物品,在选择第j件物品,那么:
(goods[i].price + goods[j].minmoney) < (goods[j].price + goods[i].minmoney)
也即
goods[i].minmoney - goods[i].price > goods[j].minmoney - goods[j].price

所以就按照(goods[i].minmoney - goods[i].price)由大到小的顺序将其排序后直接成为简单的01背包即可
(亲测31MS正确)
#include <cstdio>#include <algorithm>#include <cstring>#define maxn 5005using namespace std;struct Goods{int value;int price;int minmoney;/*低于该价格不售出*/ /*且minmoney >= price*/ };/*商品:goods*/ int dp[maxn];/*dp[i]表示i元钱能购买的最大价值*/ struct Goods goods[5005];int cmp(struct Goods a,struct Goods b){return a.minmoney - a.minmoney < b.minmoney - b.minmoney ;}int main(){int n,totalmoney;while(scanf("%d%d",&n,&totalmoney) != EOF){memset(dp,0,sizeof(dp));memset(goods,0,sizeof(goods));for(int i = 0 ; i < n ; i++)scanf("%d%d%d",&goods[i].price,&goods[i].minmoney,&goods[i].value);sort(goods,goods+n,cmp);for(int i = 0 ; i < n ; i ++)for(int j = totalmoney ; j >= goods[i].minmoney  ; j--)dp[j] = max(dp[j],dp[j-goods[i].price] + goods[i].value);printf("%d\n",dp[totalmoney]);}return 0;}






1 0
原创粉丝点击