ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

动态规划的无后效性

2021-07-11 20:59:44  阅读:363  来源: 互联网

标签:无后效 curp int next gg curtime 动态 规划 dp


写这篇的起因还是因为 2021.7.10 力扣双周赛 的第四题用 记忆化 + 深搜 来写的,但是一直有几个样例过不去,在困扰之际看到了另一篇的题解 为什么记忆化搜索得不到正解? 中才恍然大悟,特此做一个记录

无后效性。所谓的无后效性,即对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策。

题目描述 :

 

 比赛思路:

用一个邻接表来表示图,g[i] = {p0,...,p_j} 与 点 i 相邻的点为 {p0,...,p_j}

gg[i][j] 来表示 点 i 和 点 j 之间的时间开销

dp[curp][curtime] 在点 curp ,且已经用时 curtime 的情况下,到达终点的最小开销

 则 : dp[0][0] = max{ dp[j][gg[0][j] } 往下一直深搜即可

看上去好像没有什么问题,但是 :

对于这样这种 a - > d 的深搜 

(1)从a出发访问b,接下来访问c和d,实际访问路径为a->b->d和a->b->c->d.
(2)从a出发访问c,直接返回memo[c],实际访问路径为a->c->d.

不难发现,访问路径缺少了a->c->b->d,为什么会这样呢?

原因在于(1)中,从b访问c时计算的memo[c]并非是c到d的最大概率。c到d共有c->d和c->b->d两条路径,由于b会被标记为已访问,因而无法从c到b,所以无法保证memo[c]的正确性。更进一步,上述过程并不满足动态规划中的无后效性。所谓的无后效性,即对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策。

 所以即使是我设的是 dp[curp][curtime] 两个维度,若有两条路到 curp 这个点的时候 用时都是 curtime ,比如上图中的 c 这个点,那么该点的 c 并不一定是最小的,因为有前驱的值来限制它,所以这种方法的正确性并不能保证,即 动态规划的后效性无法保证

比赛时的代码:

#define x first
#define y second
#include <iostream>
#include <bits/stdc++.h>
#include <ctime>
#include <algorithm>
#include <stdlib.h>
using namespace::std;
using LL = long long;
using PII = pair<int,int>;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
class Solution {
public:
    vector<vector<int>> g;
    int n;
    int dp[1005][1005];// dp[i] i 到 n - 1 的最小花费 - 1 表示不可达
    int maxTime;
    bool vi[1005];
    int gg[1005][1005];
    int dfs(int curtime,int curp,vector<int>& pf) {
        if(dp[curp][curtime] != INF) {
            return dp[curp][curtime];
        }// 若之前已经求过
        if(curp == n - 1) {
            return pf[n - 1];
        }
        int res = INF;
        for(auto& next : g[curp]) {
            if(!vi[next]) {
                if(curtime + gg[curp][next] > maxTime) continue;
                vi[next] = true;
                int t = dfs(curtime + gg[curp][next],next,pf);
                if(t != -1)
                    res = min(res,t);
                vi[next] = false;
            }
        }
        if(res != INF)
            res += pf[curp];
        dp[curp][curtime] = res == INF ? -1 : res;
        return dp[curp][curtime];
    }
    int minCost(int maxTime_, vector<vector<int>>& edges, vector<int>& pFees) {
        n = pFees.size();
        g.resize(n);
        maxTime = maxTime_;
        // 初始化
        for(int i = 0;i < n;i ++) {
            memset(dp[i],INF,4 * 1005);
            vi[i] = false;
            memset(gg[i],-1,4 * n);
        } 
        for(auto& e : edges) {
            if(gg[e[0]][e[1]] == -1) {
                g[e[0]].push_back(e[1]);
                g[e[1]].push_back(e[0]);
                gg[e[0]][e[1]] = e[2];
                gg[e[1]][e[0]] = e[2];
            } else {
                gg[e[0]][e[1]] = min(gg[e[0]][e[1]],e[2]);
                gg[e[1]][e[0]] = gg[e[0]][e[1]];
            }
            
        }
        vi[0] = true;
        int t = dfs(0,0,pFees);
        return t;
    }
};

 正确代码 :

// author : wyx
#define x first
#define y second
#include <iostream>
#include <bits/stdc++.h>
#include <ctime>
#include <algorithm>
#include <stdlib.h>
using namespace::std;
using LL = long long;
using PII = pair<int,int>;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
class Solution {
public:
    vector<vector<int>> g;// 邻接表
    int n;// 点的个数
    int dp[1005][1005];// - 1 表示不可达
    // dp[i][j] 在 j 这个点,总用时为 i 的时候所产生的最小开销
    int gg[1005][1005];// 两个点的距离
    int minCost(int maxTime, vector<vector<int>>& edges, vector<int>& ps) {
        n = ps.size();
        g.resize(n);
        // 初始化
        for(int i = 0;i < n;i ++) {
            memset(dp[i],-1,4 * 1005);
            memset(gg[i],-1,4 * n);
        }
        // 初始化图
        for(auto& e : edges) {
            if(gg[e[0]][e[1]] == -1) {
                g[e[0]].push_back(e[1]);
                g[e[1]].push_back(e[0]);
                gg[e[0]][e[1]] = e[2];
                gg[e[1]][e[0]] = e[2];
            } else {
                gg[e[0]][e[1]] = min(gg[e[0]][e[1]],e[2]);
                gg[e[1]][e[0]] = gg[e[0]][e[1]];
            }
            
        }
        for(int i = 0;i <= maxTime;i ++) dp[0][i] = ps[0];
        for(int t = 0;t <= maxTime;t ++) {
            for(int j = 0;j < n;j ++) {
                if(dp[j][t] != -1) {
                    // 说明在时间 t 能到达 点 j 然后从 j 开始转移
                    for(auto& next : g[j]) {
                        if(gg[next][j] + t > maxTime) continue;
                        if(dp[next][t + gg[next][j]] == -1 || \
                        dp[next][t + gg[next][j]] > dp[j][t] + ps[next]) {
                            dp[next][t + gg[next][j]] = dp[j][t] + ps[next];
                        }
                    }
                }
            }
        }
        return dp[n - 1][maxTime];
    }
};

比赛总结:

对于动态规划还是要多理解,对于无特效性也还是要多做理解,这样才能更好地理解和运用动态规划 :D

 

标签:无后效,curp,int,next,gg,curtime,动态,规划,dp
来源: https://blog.csdn.net/hdu_wyx/article/details/118658625

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有