hdu 3466 要排序的动归

来源:互联网 发布:域名隐藏转发 编辑:程序博客网 时间:2024/06/08 16:22

题意 给定 n个物品 每个物品有 p q v 必须满足q才能买这个物品,求价值最大

两天时间的思考,
首先要理解无后效性,动归的一个应用条件就是无后效性,在普通背包里面,是否排序它都是无后效性的,我觉得理解的时候得按照现实和代码两种理解,因为代价和价值相等,所以在dp数组里面是否排序,其实在决策之间是不会相互影响的,把这个阶段买不买的最优情况记录下来,然后下个阶段也会有根据当前最优阶段的买不买的情况,而且在这个普通01背包过程中,买不买都会有相应的情况表示,而不会有如果你这个阶段买了这个东西,导致下个阶段即使代价足够,也不能购买这个东西,如果有这种情况,那么就说这个东西是有后效性,引用这么一段话,后效性就是这个阶段的过程演变与以后(未来)无关,不会因为未来而改变。即这个阶段的最优是不会变的。这是人思维的理解,而在代码上,因为这道题我看了好多好多的解答。不外乎两种理解。逆向理解。由于01背包其实是逆向的,所以你要先买的东西是在后面往前决定的。
原因在于,
如果你通俗的说: 这个dp过程 正好是逆向的 。 dp[c-p[i]]是dp[c]的最优子结构。
也可以理解成 前面所有的状态前面基本上把所有决策的情况都考虑到了,而每一次循环都是在优化所有的情况,而当最后一个循环,在代码里面最后(其实是思维中最先买的物品)确定。一个最优解就被拼接而成。dp[c-p[i]]是dp[c]的最优子结构,从而把最优的dp[m]决定。所以动态规划是一个没有后效性的,这个阶段的所有最优与上一个阶段有关,但是不会改变上一个阶段的最优。最后的最优决定以后,不一定它的前一步每一步都是最优(只能说是总体最优,或者说i-1阶段所有决策是对于所有决策的最优)。确定了最后一步(相当于人思维的第一步),那么它所走的路就是最优解的子结构。那么下午说如何排序,摘抄

分析:

商品如果没有属性q的话就是一个单纯的01背包,正是因为该属性,如果再像01背包那样求解的话就有了后效性,因为很可能存在这种情况:

先买物品1就买不了物品2,但是如果先买物品2就可以继续买物品1

下面我们来找一下出现这种情况的条件是什么:
01 p1==q1 p2==q2,没影响。
假设现在又两件物品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 的值大的
比如,你有13块钱,p1 = 5,q1 = 11,w1 = 3,p2 = 3,q2 = 8,w2 = 4

按照错误方式排序:

这里写图片描述

按照正确方式排序:
这里写图片描述

这东西虽然想了很多,但最后还是会忘的,因为太难以准确了。
归纳一下:如果你想要根据每步都是最优后面不会被前面影响理解,01背包的每步最优是逆序的,从最后一个循环开始最优。所以要拿差值最大的开始,就把差值最大的放最后。
二 如果你想要根据,for的正序理解,你就应该知道,每一步for其实是把所有的情况都列出来,等到最后的for一确定,前面整条路都确定了。但它不一定是前面每个阶段最优的dp[m],它可能是任何一个dp。
三 消除差值带来的负面影响,差值影响了01背包的正常步骤。,消除后效性。排序的目的就是为了消除差值带来的负面影响,差值越大负面影响就越大,一开始差值少影响少)而dp,很明显就是要尽量多的运用更新了的区间,累加起来的价值才最大。所以把更新大的放前面是对的。
三种方法,随便一种都能理解。三种都知道就会很烦躁。(最后一种的正确性很难确定,毕竟问题太多。)

代码

#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>using namespace std;const int MAXN=550;struct Node{    int p,q,v;}node[MAXN];int dp[5500];bool cmp(Node a,Node b)//按照 q-p 从小到大排序{    return a.q-a.p < b.q-b.p;}int main(){    int n,m;    while(scanf("%d%d",&n,&m)!=EOF)    {        for(int i=0;i<n;i++)          scanf("%d%d%d",&node[i].p,&node[i].q,&node[i].v);        sort(node,node+n,cmp);        memset(dp,0,sizeof(dp));        for(int i=0;i<n;i++)          for(int j=m;j>=node[i].q;j--)            dp[j]=max(dp[j],dp[j-node[i].p]+node[i].v);        printf("%d\n",dp[m]);    }    return 0;}

最后我想说 我可能学了假dp

0 0