zoukankan      html  css  js  c++  java
  • 第十六关——动态规划

    2020-05-04 11:02:59 下课铃虽晚,还好你步调慢,路虽长,想陪你都走完。——费启鸣《刘大志的歌》

    五一匆匆过,就是不知半期何时来(不来最好),不过呢,我们还是要做好合适的动态规划。(最近还有一些比较纠结....的事情,那就好好做好动态规划吧)

    这里附赠一个网站,有全套范围

    动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法

    动态规划解题的一般思路:

    1.将原问题分解成子问题,注意这个是否是一个独立的子问题,确定问题的最优解在子问题上也是最优的

    2.确定状态,用状态把分解出来的问题尽可能定义出来

    3.确定方程的边界状态,这是方程递推的基础,很多时候边界都会出问题

    4.确定状态转移方程,考虑细致全面,不重复不遗漏,避免形成环状影响(DP的状态转移图是有拓扑序的),注意是否有后效性

    2020-05-26 18:45:48 也许惦念好久的眷恋  不过一生中惊鸿一瞥——文橘《二三事》

    •  01背包

    题目描述:
    给定 n 个物品和一个容量为 W 的背包,物品 i 的重量是 wi,其价值为 vi ,应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?(输出给定n和W时的最大总价值)

    1、物品的顺序是固定的

    同国王与金矿的问题一样,我们认为,物品的顺序是固定的,这样 才能够合理的写出最优子结构。

    2、要考虑背包重量为0的情况(即使题目说了背包重量大于0)

    我们假设F(n,w)代表物品为第n件结尾,背包空间为w时,所能够装下的最大价值。
    那么,F(1,0)也是一定要存在的,表示只有一一个物品,但承重为0的情况!

    因为在最优子结构中,可能会出现F(n-1,W-wi)+vi中,W-wi等于0的情况,此时前面的F(n,0)的值也应该为0,
    此时,最有子结构的值就等于后面的被加数vi了。

    因此,在构造一维数组时,需要初始化为res=[0],再陆续添加重量为1,2,...,w的情况。(其实在国王与金矿构造的一维数组中,也需要考虑人数为0的情况的。)

    3、构造数组时,要注意与重量列表中的重量一致

    4、状态转移方程

    • 当W-wi大于0时,F(n,w) = max(F(n-1,W-wi)+vi,F(n-1,w))

    • 当W-wi小于0时,F(n,w) = F(n-1,w))

      先初始化一行,然后进行两个for循环,不断地滚动这两行,一直到最后的结果

    博客详细

    背包九讲

    •  序列dp

    状态设计思路:

    1.既然是序列,那么一般都会有一个i表示做到第i项,如果是有两个序列,那么也会有一个j表示做到B序列的第j项,此时枚举是枚举下标的

    2.如果题目给出k次机会之内的也要把状态记录下来,但是此时由于不知道什么时候用的机会,所以会类似定义发f[i][k]为用了k次机会,最后一次用在i身上,通常用来辅助转移g[i][k]表示用了k次机会,最后一次不一定用在i上

    3.有些类似合并类的题目状态从前往后做无法表示,会设成f[i][j]表示第i项合并到第j项的代价,这时由于区间是不断拓宽的,只能先枚举区间的宽度再枚举左端点并确定右端点

    4.对于一些大小为n的排列在规定条件下不同方案的问题,此时f[i][j]可以表示做到第i个,用了前j个数,最后一个用j。这时要把新来的i插进去,f[i-1]状态表示的排列很简单,如果最后一个数是k,把>=k的数全部加1就好,原本f[i-1]表示的排列在[1,i-1]内,>=k的数加了个1范围[1,i]

    最长公共子序列(Least common Sequence)

    给定两个字符串 s1 和 s2,求两个字符串的最长公共子序列。

    子序列是一个字符串不连续的序列,如对于字符串 [公式] 的一个子序列可以是 [公式]

    那么,对于最长公共子序列,我们可以定义这样的一个状态 [公式],代表 [公式] (即 [公式]的前 [公式] 个字符)和 [公式] 最长公共子序列。需要注意到,上面的区间是前闭后开的。这样写的一个好处是,可以避免处理像 0 这样的边界。

    那么,其对应的状态的转移方程可以表述如下:

    [公式]

    2020-05-31 15:43:15 I'm the one you're my kryptonite——刘思鉴《在你身边就失去超能力》

    •  线性dp

    最长上升子序列问题

    给定一个长度为N的的数列A,求数列单调递增的子序列长度最长是多少。

    f[i]=max(f[i],f[j]+1)

    边界:f[0]=0 目标:max(f[i])

    数字三角形

    给定一个共有n行的三角矩阵A,其中第i行有j列。从左上角出发,每次可以向下方或右下方走一步,最终到达底部

    if(j>1) f[i][j]=a[i][j]+max(f[i-1][j],f[i-1][j-1])

    边界:f[1][1]=a[1][1] 目标 max(f[n][j])

    • 区间dp

    f[l][r] 表示一段连续区间 [l,r ] 上的答案

    1序列: 区间 [l,r] 的最后一步处理的是 k 元素 / 分割点是 k / 受 k 控制的 常用四边形不等式优化

    2环: 复制一下粘到后面, 枚举断点

    3点集回路: 利用线段不交的性质按顺序 dp

    • 期望dp

     概率Dp是一类求事件概率或期望的Dp的总称,对于求 概率问题,有时利用补集转化,或者将其转化为计数问题, 而对于求期望则大多利用期望的线性性来解决问题

    • 环形dp

    通常有两种解决办法:

    1.执行两次dp。第一次在任意位置把欢断成链,按照线性来解,第二次再把链相连

    2.把环断成链,然后复制一背在末尾。

    题目大意:给定一个环上有n个数,分成m个部分,求这m个部分的和相乘的最大值和最小值。

    f[i][j][k]表示在l到r中分成k段的最值

    fmax[l][r][i]=max(fmax[l][r][i],fmax[l][k][i-1]*fmax[k+1][r][1])

    fmin[l][r][i]=min(fmin[l][r][i],fmin[l][k][i-1]*fmin[k+1][r][1])

    注意:环形dp要把数组开大一倍

    •  树形dp

    由于树有着天然的递归结构 父子结构 而且它作为一种特殊的图 可以描述许多复杂的信息 因此在树就成了一种很适合DP的框架
    问题:给你一棵树 要求用最少的代价(最大的收益)完成给定的操作
    树形DP 一般来说都是从叶子从而推出根 当然 从根推叶子的情况也有 不过很少
    一般实现方式: DFS(包括记忆化搜索),递推等

    最大独立子集

    最大独立子集的定义是,对于一个树形结构,所有的孩子和他们的父亲存在排斥,也就是如果选取了某个节点,那么会导致不能选取这个节点的所有孩子节点。
    询问是让你给出这颗树的最大独立子集的大小。思路:对于任意一个节点,他都有两种选择:
    A . 选择:A选择,那么他的孩子必定不能要,此时对于A和A的孩子们来说,能构成的最大独立子集是1+∑A.son.notcome。
    B . 不选择:A不选择,那么他的孩子们可来可不来,此时对于A和A的孩子们来说,能构成的最大独立子集是
    for i 1->son.size  father.notcome+=max(son.notcome,son.come);
    当然可以在这基础上加以变化,比如给点设置权值,要你求得这个最大独立子集是满足有最大权值和的。

    最优连通子集

    Description
           众所周知,我们可以通过直角坐标系把平面上的任何一个点P用一个有序数对(x, y)来唯一表示,如果x, y都是整数,我们就把点P称为整点,否则点P称为非整点。我们把平面上所有整点构成的集合记为W。 定义1 两个整点P1(x1, y1), P2(x2, y2),若|x1-x2| + |y1-y2| = 1,则称P1, P2相邻,记作P1~P2,否则称P1, P2不相邻。 定义 2 设点集S是W的一个有限子集,即S = {P1, P2,..., Pn}(n >= 1),其中Pi(1 <= i <= n)属于W,我们把S称为整点集。 定义 3 设S是一个整点集,若点R, T属于S,且存在一个有限的点序列Q1, Q2, ?, Qk满足: 1. Qi属于S(1 <= i <= k); 2. Q1 = R, Qk = T; 3. Qi~Qi + 1(1 <= i <= k-1),即Qi与Qi + 1相邻; 4. 对于任何1 <= i < j <= k有Qi ≠ Qj;
    我们则称点R与点T在整点集S上连通,把点序列Q1, Q2,..., Qk称为整点集S中连接点R与点T的一条道路。 定义4 若整点集V满足:对于V中的任何两个整点,V中有且仅有一条连接这两点的道路,则V称为单整点集。 定义5 对于平面上的每一个整点,我们可以赋予它一个整数,作为该点的权,于是我们把一个整点集中所有点的权的总和称为该整点集的权和。
    我们希望对于给定的一个单整点集V,求出一个V的最优连通子集B,满足: 1. B是V的子集 2. 对于B中的任何两个整点,在B中连通;
    3. B是满足条件(1)和(2)的所有整点集中权和最大的。
    Input
    第1行是一个整数N(2 <= N <= 1000),表示单整点集V中点的个数;
    以下N行中,第i行(1 <= i <= N)有三个整数,Xi, Yi, Ci依次表示第i个点的横坐标,纵坐标和权。同一行相邻两数之间用一个空格分隔。-10^6 <= Xi, Yi <= 10^6;-100 <= Ci <= 100。
    Output
    仅一个整数,表示所求最优连通集的权和。
    Sample Input
    5
    0 0 -2
    0 1 1
    1 0 1
    0 -1 1
    -1 0 1
    Sample Output
    2
    题目很繁琐,该题大意为:
    给定一个平面整点集,点与点间在|x1-x2| + |y1-y2| = 1时相邻,且形成的图没有回路, 每个点有一个可正可负的权值,求最大权和连通子图.

    /* 给定的是一颗树,根据题意,我们可以从任意一个节点出发,必能访问到其他所有节点,那么dp的起点可以在任意一个节点。 我们从该起点出发,对以此点为根的树的每个分支进行搜索,采用树的后续遍历法则,对于每个子树来说,dp值首先加上根节点 (因为要保证连通性,所以返回值中必须包含根节点的值,即使为负数也必须加上)先对每个分支dp,然后看分支dp的返回值是不是正数, 如果是正数,那么我们就把该分支的返回值加入到该树中去。 就是每个子树的根节点(包括叶子节点)记录dp[i][0]与dp[i][1],前一个表示不包含根的最大值,后一个表示包含根的最大值。 那么我们可以得到对于dp[i][0],必然是所有分支中dp[child][0]与dp[child][1]中大于0的最大值的累加 (因为不包含树根,所以在根节点上的连通性不用保证), dp[i][1]必然是所有分支中dp[child][1]中大于0的最大值的累加再加上该树根本身的值(因为要保证连通性)。 最后只要比较dp[root][0]与dp[root][1],输出较大。 */
    #include <stdio.h>
    #include <iostream>
    using namespace std;
    int Abs(int x) {
        return x>=0?x:-x;
    }
    int max(int x,int y,int z) {
        if(x<y) x=y;
        if(x<z) x=z;
        return x;
    }
    int max(int x,int y) {
        return x>y?x:y;
    }
    struct P {
        int x,y,c;
        void input() {
            scanf("%d%d%d",&x,&y,&c);
        } bool isConnect(P & t) {
            if(Abs(x-t.x)+Abs(y-t.y)==1) return 1;
            return 0;
        }
    } p[1005];
    struct Edge {
        int v,next;
    } edge[10005];
    int edgeNum,head[1005];
    int dp[1005][2];
    void addEdge(int u,int v) {
        edge[edgeNum].v=v;
        edge[edgeNum].next=head[u];
        head[u]=edgeNum++;
    }
    void dfs(int x,int father) {
        dp[x][0]=0;
        dp[x][1]=p[x].c;
        for(int i=head[x]; i!=-1; i=edge[i].next) {
            if(edge[i].v!=father) {
                dfs(edge[i].v,x);
                dp[x][0]=max(dp[x][0],dp[edge[i].v][0],dp[edge[i].v][1]);
                if(dp[edge[i].v][1]>0) {
                    dp[x][1]+=dp[edge[i].v][1];
                }
            }
        }
    }
    int main() {
        int n;
        while(scanf("%d",&n)!=EOF) {
            for(int i=0; i<n; i++) {
                head[i]=-1;
                p[i].input();
            }
            edgeNum=0;
            for(int i=0; i<n; i++) for(int j=i+1; j<n; j++) {
                    if(p[i].isConnect(p[j])) {
                        addEdge(i,j);
                        addEdge(j,i);
                    }
                }
            dfs(0,-1);
            printf("%d
    ",max(dp[0][0],dp[0][1]));
        }
    }
    View Code
    //poj 1192 最优连通子集 (树型DP) /* 题意:给定一个平面整点集,点与点间在|x1-x2| + |y1-y2| = 1时相邻,且形成的图没有回路, 每个点有一个可正可负的权值,求最大权和连通子图。 题解:树型DP,dp[i][0]表示以i为根的子树上不包括i的最大权和,dp[i][1]表示包括i的最大权和。 */
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    using namespace std;
    const int inf = 1<<28;
    int n,m;
    struct Point {
        int x,y,c;
    } p[1010];
    vector<int> con[1010];
    int dp[1010][2],mark[1010];
    void dfs(int v) {
        mark[v]=1;
        dp[v][0]=0,dp[v][1]=p[v].c;
        int j;
        for (int i=0; i<con[v].size(); i++) if (!mark[con[v][i]]) {
                j=con[v][i];
                dfs(j);
                dp[v][0]=max(dp[v][0],max(dp[j][0],dp[j][1]));
                if (dp[j][1]>0) dp[v][1]+=dp[j][1];
            }
    }
    int main() {
        scanf("%d",&n);
        for (int i=0; i<n; i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].c);
        for (int i=0; i<n; i++)
            for (int j=i+1; j<n; j++)
                if (abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y)==1) {
                    con[i].push_back(j);
                    con[j].push_back(i);
                }
        dfs(0);
        printf("%d/n",max(dp[0][0],dp[0][1]));
        return 0;
    }
    View Code

     优秀博客鉴赏

    one.内容详细

    two.类型讲解

    three.图文介绍

    four.轻松一刻

  • 相关阅读:
    word-wrap与word-break为长单词换行
    background-origin与background-clip的“区别”
    box-shadow
    你不知道的border-radius
    当document.write 遇到外联script
    getAttribute()方法的第二个参数
    Backbone.js入门教程第二版笔记(3)
    Backbone.js入门教程第二版笔记(2)
    php中的字符串常用函数(二) substr() 截取字符串
    php中类和对象的操作
  • 原文地址:https://www.cnblogs.com/wybxz/p/12825796.html
Copyright © 2011-2022 走看看