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

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 10 10 15 10 5 10 5 3 10 5 10 5 3 5 6 2 7 3
 

Sample Output
 
  
5 11
开始的时候没多想,以为这个题目很简单,于是写下了这样的代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 5005
using 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 5005
using 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;
}






转载于:https://www.cnblogs.com/sixdaycoder/p/4348398.html

### 动态规划解决0-1背包问题的状态转移方程 对于0-1背包问题,状态转移方程是解决问题的核心部分。设`dp[i][j]`表示前i个物品放入容量为j的背包可以获得的最大价值。 当不选择第i个物品时,最大价值等于前i-1个物品在同样容量下的最大价值;如果选择了第i个物品,则其价值加上剩余空间所能容纳的价值总和构成新的可能方案。因此有: \[ dp[i][j]=\max(dp[i−1][j],v_i+dp[i−1][j-w_i]) \] 其中\( v_i \)代表第i件商品的价值,而 \( w_i \) 表示该商品所占的空间大小[^1]。 为了更直观理解上述公式,在具体应用过程中可以考虑如下情况:给定n种不同的物品以及它们各自的体积w[]和价值v[],还有一个固定容量W的背包。目标是在不超过包载体积的前提下使携带物品总价值最高。 这里给出一段基于此逻辑编写的Python版本伪代码作为例子说明如何构建解决方案框架: ```python def knapsack(weights, values, capacity): n = len(values) # 创建二维数组存储中间结果 dp = [[0]*(capacity + 1) for _ in range(n + 1)] for i in range(1, n + 1): for j in range(capacity + 1): if weights[i-1] <= j: dp[i][j] = max(dp[i-1][j], values[i-1]+dp[i-1][j-weights[i-1]]) else: dp[i][j] = dp[i-1][j] return dp[n][capacity] ``` 这段程序通过迭代计算每一个位置上的最佳解并记录下来,最终返回整个表格右下角元素即为所求答案——能够装载入指定容积内的最大价值组合[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值