HDU 3466 (排序消除后效性的01背包) Proud Merchants
题意:
你有m块钱,有n件商品,每件商品有它的价格p 要买这件商品至少拥有的钱数q和价值w,问能获得的最大价值是多少
分析:
商品如果没有属性q的话就是一个单纯的01背包,正是因为该属性,如果再像01背包那样求解的话就有了后效性,因为很可能存在这种情况:
先买物品1就买不了物品2,但是如果先买物品2就可以继续买物品1
下面我们来找一下出现这种情况的条件是什么:
假设现在又两件物品1和2,你手中有k块钱,而且如果先买1的话能买2,但是先买2的话不能买1,则有:
p1 + q2 ≤ k < p2 + q1
移项得,q1 - p1 > q2 - p2
好的,如果两件商品满足q1 - p1 > q2 - p2,那么一定存在一个或多个k值满足 p1 + q2 ≤ k < p2 + q1 ,所以我们在考虑买物品的时候一定要考虑 q减p 的值大的
可是如果你按照q-p从大到小排序的话,就又错了,囧rz
下面我来照搬一下别人博客上的话:
通俗的说: 这个dp过程 正好是逆向的 。 dp[c-p[i]]是dp[c]的最优子结构。
所以 在DP 的时候我们应该按照 M的值从小到大排序。
我还不能理解太多,但我找了个例子试了一下,真的是这样的
比如,你有13块钱,p1 = 5,q1 = 11,w1 = 3,p2 = 3,q2 = 8,w2 = 4
按照错误方式排序:

按照正确方式排序:

这的好神奇,不能理解更多。。
1 //#define LOCAL 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn = 5000 + 10; 8 9 struct Trade 10 { 11 int p, q, w; 12 bool operator< (const Trade& rhs) const 13 { 14 return (q - p) < (rhs.q - rhs.p); 15 } 16 }trades[maxn]; 17 int n, m, dp[maxn]; 18 19 int main(void) 20 { 21 #ifdef LOCAL 22 freopen("3466in.txt", "r", stdin); 23 #endif 24 25 while(scanf("%d%d", &n, &m) == 2) 26 { 27 memset(dp, 0, sizeof(dp)); 28 for(int i = 1; i <= n; ++i) 29 scanf("%d%d%d", &trades[i].p, &trades[i].q, &trades[i].w); 30 sort(trades + 1, trades + 1 + n); 31 for(int i = 1; i <= n; ++i) 32 for(int j = m; j >= trades[i].q; --j) 33 dp[j] = max(dp[j], dp[j - trades[i].p] + trades[i].w); 34 printf("%d\n", dp[m]); 35 } 36 return 0; 37 }

浙公网安备 33010602011771号