插头DP小结?
因为独立插头还有最小表示法没有打过,独立插头没看懂,广义路径连看都没看过,所以这些不再本文的总结范围内。
因为独立插头还有最小表示法没有打过,独立插头没看懂,广义路径连看都没看过,所以这些不再本文的总结范围内。
去年学插头DP的时候还是很惧怕的,也只做了一题,还是按着课件对着标程打的。还好今年终于懂得原理了。其实打多了也变成模板了(当然每题的转移不一样)。
插头DP有下面的分类:
按模型:多回路问题、单回路问题、简单路径问题、广义路径问题
按表示方法:是否有插头、括号法(广义括号法)、最小表示法
插头DP最朴素的实现就是用f[i][j][k]记下在(i,?j)这个点轮廓线状态为k的答案。空间上很大,所以一般用队列实现了。而且队列实现可以免去动规数组初始化的麻烦。队列实现需要一个hash,对于内存比较宽裕的题目,可以直接开一个桶解决.
参考资料
基于连通性状态压缩的动态规划问题?- 陈丹琦
多回路问题
这类问题是最水的,只要记录当前位置有没有插头即可。比如说hdu1693。
有上插头和左插头→没有右插头或下插头
没有上插头或做插头→有右插头和下插头
只有一个插头→右插头和下插头分别只有一个
单回路问题
这类问题得维护连通性使得只存在一个联通快。具体的做法课件上讲的很清楚。例题有:ural1519?Formula?1poj1739 (如果在外面加一圈障碍的话,就变成上面那题了)
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;
}