插头dp

来源:互联网 发布:去除视频马赛克软件 编辑:程序博客网 时间:2024/05/17 03:49
插头DP小结?

因为独立插头还有最小表示法没有打过,独立插头没看懂,广义路径连看都没看过,所以这些不再本文的总结范围内。

去年学插头DP的时候还是很惧怕的,也只做了一题,还是按着课件对着标程打的。还好今年终于懂得原理了。其实打多了也变成模板了(当然每题的转移不一样)。

插头DP有下面的分类:

按模型:多回路问题、单回路问题、简单路径问题、广义路径问题

按表示方法:是否有插头、括号法(广义括号法)、最小表示法

插头DP最朴素的实现就是用f[i][j][k]记下在(i,?j)这个点轮廓线状态为k的答案。空间上很大,所以一般用队列实现了。而且队列实现可以免去动规数组初始化的麻烦。队列实现需要一个hash,对于内存比较宽裕的题目,可以直接开一个桶解决.

参考资料

基于连通性状态压缩的动态规划问题?- 陈丹琦


多回路问题

这类问题是最水的,只要记录当前位置有没有插头即可。比如说hdu1693。

有上插头和左插头→没有右插头或下插头

没有上插头或做插头→有右插头和下插头

只有一个插头→右插头和下插头分别只有一个


单回路问题

这类问题得维护连通性使得只存在一个联通快。具体的做法课件上讲的很清楚。例题有:ural1519?Formula?1

poj1739 (如果在外面加一圈障碍的话,就变成上面那题了)

bzoj1187(这题允许任意起点,并且不要求遍历全图)

限制起点终点的简单路径问题

简单路径问题按理来说应该用独立插头来解决,但是这类问题因为限制了起点和终点,所以比较好解决,只要对每个格子的类型进行判断,根据不同的格子进行不同的转移即可。比如说:

poj3133(这题网上都说是独立插头,但是直接特判格子就可以过掉了)

poj1739(这题如果不加一圈的话,用特判的方法还是很好写的)


#include <bits/stdc++.h>//http://www.lydsy.com/JudgeOnline/problem.php?id=1187#define maxn 110using namespace std;typedef long long ll;int n, m;int a[maxn][maxn];ll ans = -1ll << 60;#define M 2000010#define mod 997 struct Hashmap{         ll st[M];    int h[1000], size, nxt[M];    ll f[M];    void clear(){memset(h, 0, sizeof h); size = 0;}    void push(ll hash_, ll val){        int tmp = hash_ % mod;        for(int i = h[tmp]; i; i = nxt[i]){            if(st[i] == hash_){                f[i] = max(f[i], val);                return;            }        }        int now = ++ size;        f[now] = val;        st[now] = hash_;        nxt[now] = h[tmp];        h[tmp] = now;    }}dp[2];  int cur, code[20], ch[20]; void Decode(ll st){    for(int i = m; i >= 0; i --)        code[i] = st & 7, st >>= 3;} ll Encode(){    ll ret = 0;    memset(ch, -1, sizeof ch);    ch[0] = 0; int cnt = 0;    for(int i = 0; i <= m; i ++){        if(ch[code[i]] == -1)ch[code[i]] = ++ cnt;        code[i] = ch[code[i]];        ret = ret << 3 | code[i];    }    return ret;} void Shift(){    for(int i = m; i >= 1; i --)        code[i] = code[i-1];    code[0] = 0;}   inline void Change(int u, int v){    for(int i = 0; i <= m; i ++)        if(code[i] == v)            code[i] = u;} void DP(int i, int j){    dp[cur^1].clear();    for(int k = 1; k <= dp[cur].size; k ++){        Decode(dp[cur].st[k]);        if(j == 1){if(code[m])continue;Shift();}        int Left = code[j-1], Up = code[j];        if(Left && Up){            code[j] = code[j-1] = 0;            if(Left == Up){                ll ENCODE = Encode();                if(ENCODE == 0)ans = max(ans, dp[cur].f[k] + a[i][j]);            }            else{                Change(Left, Up);                dp[cur^1].push(Encode(), dp[cur].f[k] + a[i][j]);            }        }        else if(Left || Up){            int tmp = Left ? Left : Up;            code[j-1] = 0, code[j] = tmp;            dp[cur^1].push(Encode(), dp[cur].f[k] + a[i][j]);            code[j] = 0, code[j-1] = tmp;            dp[cur^1].push(Encode(), dp[cur].f[k] + a[i][j]);        }        else{            dp[cur^1].push(Encode(), dp[cur].f[k]);            code[j] = code[j-1] = 8;            dp[cur^1].push(Encode(), dp[cur].f[k] + a[i][j]);        }    }    cur ^= 1;}  int main(){#ifndef ONLINE_JUDGE    freopen("park.in","r",stdin);    freopen("park.out","w",stdout);#endif    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i ++)        for(int j = 1; j <= m; j ++)            scanf("%d", &a[i][j]);    dp[cur].clear();    dp[cur].push(0, 0);    for(int i = 1; i <= n; i ++)        for(int j = 1; j <= m; j ++)            DP(i, j);         printf("%lld\n", ans);    return 0;}