hdu 3466 要排序的动归

题意 给定 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值