插头dp

插头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 110
using 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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值