POJ 3133
第一次了解插头DP,后来一点点索引到了CDQ大佬那篇08年度集训队论文,只是看了一点点开头,思路确实很精妙,orz。然而开始还并没领会,参考了部分别的犇的代码,才有了整体框架思路,关于进制和哈希方法的套路才得以窥知一二。
进位制采用\(2^n\)进制,即使冗余,但这从计算机角度思考才是最优的.
这篇题解写的太粗略了,留个坑补下吧
#include <iostream>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N= 12;
const int inc= 2;
const int HASH= 1e5+7;
const int STATE= 1e6+10;
int bd[N][N], dp[2][1<<(N<<1)];
int code[N];
int n, m;
struct HashMap
{
int head[HASH];
int next[STATE], state[STATE], dp[STATE];
int sizes;
void init()
{
sizes= 0;
memset(head, -1, sizeof(head));
}
void push(int st, int ans)
{
int h= st%HASH;
for (int i= head[h]; -1!= i; i= next[i]){
if (st== state[i]){
if (ans< dp[i]){
dp[i]= ans;
}
return;
}
}
dp[sizes]= ans;
state[sizes]= st;
next[sizes]= head[h];
head[h]= sizes++;
}
}hm[2];
// quad
void Decode(int code[], int m, LL st)
{
for (int i= 0; i<= m; ++i){
code[i]= st&3;
st>>= inc;
}
}
int Encode(int code[], int m)
{
int st= 0;
for (int i= m; i>= 0; --i){
st<<= inc;
st|= code[i];
}
return st;
}
void LShift(int code[], int m)
{
for (int i= m; i> 0; --i){
code[i]= code[i-1];
}
code[0]= 0;
}
void DpBlank(int cur, int i, int j)
{
int lf, up;
for (int k= 0; k< hm[cur].sizes; ++k){
Decode(code, m, hm[cur].state[k]);
lf= code[j-1];
up= code[j];
if (lf || up){
if (lf && up){
if (lf!= up){
continue;
}
else{
code[j-1]= code[j]= 0;
if (j== m){
LShift(code, m);
}
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
}
}
else{
int pre= lf ? lf : up;
if (j< m){ // ->
code[j-1]= 0;
code[j]= pre;
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
}
if (i< n){ //down
code[j-1]= pre;
code[j]= 0;
if (j== m){
LShift(code, m);
}
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
}
}
}
else{
code[j-1]= code[j]= 0; // do nothing
if (j== m){
LShift(code, m);
}
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]);
if (i< n && j< m){
code[j-1]= code[j]= 2;
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
code[j-1] =code[j]= 3;
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
}
}
}
}
void DpBlock(int cur, int i, int j)
{
for (int k= 0; k< hm[cur].sizes; ++k){
Decode(code, m, hm[cur].state[k]);
if (code[j-1] || code[j]){
continue;
}
code[j-1]= code[j]= 0;
if (j== m){
LShift(code, m);
}
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]);
}
}
void DpNum(int cur, int i, int j)
{
int lf, up;
const int num= bd[i][j];
for (int k= 0; k< hm[cur].sizes; ++k){
Decode(code, m, hm[cur].state[k]);
lf= code[j-1];
up= code[j];
if (lf&&up){
continue;
}
else if (num== lf || num== up){
code[j-1]= code[j]= 0;
if (j== m){
LShift(code, m);
}
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
}
else if (!lf && !up){
if (j<m && (0== bd[i][j+1] || num== bd[i][j+1])){
code[j-1]= 0;
code[j]= num;
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
}
if (i<n && (0== bd[i+1][j] || num== bd[i+1][j])){
code[j-1]= num;
code[j]= 0;
if (j== m){
LShift(code, m);
}
hm[cur^1].push(Encode(code, m), hm[cur].dp[k]+1);
}
}
}
}
int Solve()
{
int cur= 0;
int ans= 0;
hm[cur].init();
hm[cur].push(0, 0);
for (int i= 1; i<= n; ++i){
for (int j= 1; j<= m; ++j){
hm[cur^1].init();
if (!bd[i][j]){
DpBlank(cur, i, j);
}
else if (1== bd[i][j]){
DpBlock(cur, i, j);
}
else{
DpNum(cur, i, j);
}
cur^= 1;
}
}
for (int k= 0; k< hm[cur].sizes; ++k){
ans+= hm[cur].dp[k];
}
if (ans> 0){
ans-= 2;
}
return ans;
}
int main()
{
while (~scanf("%d %d", &n, &m) && n){
memset(bd, -1, sizeof(bd));
for (int i= 1; i<= n; ++i){
for (int j= 1; j<= m; ++j){
scanf("%d", bd[i]+j);
}
}
printf("%d\n", Solve());
}
return 0;
}