看了几天才懂,自己也说不太清楚,推荐看CDQ的论文基于连通性状态压缩的动态规划问题
这个看完之后这里有一篇比较详细的解题报告http://blog.sina.com.cn/s/blog_51cea4040100gmky.html
然后说说自己的理解吧,因为先学的轮廓线,做了几道题目,所以在状态转移上面还是比较清楚是怎么回事,这道题一直没有弄懂的是怎么用hash存状态,因为以前从来没有用过hash写题,所以卡了很久,看别人的代码后有了一点理解,如果采用最小表示法来表示,状态有每一位状态有7种,那么就需要一个三位二进制来表示一个格子,这样得到的状态用整数表示就有36位,枚举状态一定超时,其实不难发现,有很多状态都是无效的(因为不能交叉)所以题解用hash来存有效状态,从初始状态扩展出来,这样有效的避免了很多无用状态,这个状态扩展一开始也没有理解,其实这个当中的思路很像bfs 从已得到的状态来扩展其他状态。
用最小表示法的代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
#define LL long long
int mp[15][15];
const int N = 30005;
struct _Hash{
int hs[N];
LL dat[N];
int _size;
void _init(){
_size = 0;
memset(hs,-1,sizeof(hs));
}
int Push(LL s,bool &ishave)
{
int ss = s%N;
while(hs[ss] != -1 && dat[hs[ss]]!=s)
ss = (ss+1)%N;
if(hs[ss]!=-1)
{
ishave = true;
return hs[ss];
}
ishave = false;
hs[ss] = _size;
dat[_size++] = s;
return _size-1;
}
};
struct node{
_Hash hs;
LL val[N];
void _init()
{
hs._init();
memset(val,0,sizeof(val));
}
void push(LL s,LL num)
{
bool ishave;
int id = hs.Push(s,ishave);
if(ishave)
{
val[id] += num;
}
else val[id] = num;
}
}dp[2];
int n,m;
int code[15];
void deCode(LL s)
{
int tail = m+1;
while(tail--)
{
code[tail] = s&((1<<3)-1);
s>>=3;
}
}
LL enCode()
{
LL ans = 0;
int ton[10];
memset(ton,-1,sizeof(ton)); // 用于扫描 把状态表示为最小表示
ton[0] = 0;
int cnt = 1;
for(int i = 0;i<=m;i++)
{
if(ton[code[i]]==-1)
{
ton[code[i]] = cnt++;
}
code[i] = ton[code[i]];
ans <<= 3;
ans |= code[i];
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
char str[15];
memset(mp,0,sizeof(mp));
int ei,ej;
ei = ej = 0;
for(int i = 0;i<n;i++)
{
scanf("%s",str);
for(int j = 0;j<m;j++)
{
if(str[j]=='*')
mp[i][j] = 0;
else mp[i][j] = 1,ei = i,ej = j;
}
}
int cur = 0;
dp[cur]._init();
dp[1-cur]._init();
dp[cur].push(0,1);
LL ans = 0;
for(int i = 0;i<n;i++)
{
for(int j = 0;j<m;j++)
{
for(int k = 0;k<dp[cur].hs._size;k++)
{
LL sta = dp[cur].hs.dat[k];
LL num = dp[cur].val[k];
if(mp[i][j]==0)
{
dp[1-cur].push(sta,num);
continue;
}
deCode(sta);
//for(int ii = 0;ii<m;ii++)cout << code[ii] <<" ";
//cout << endl;
if(code[j]!=0 && code[j+1]!=0) // 连通
{
if(code[j]==code[j+1] && i == ei && j == ej)
{
ans += num;
}
else if(code[j]!=code[j+1])
{
int temp = code[j];
int jp = code[j+1];
code[j] = 0;
code[j+1] =0;
for(int ii = 0;ii<=m;ii++)
{
if(code[ii]==temp)
{
code[ii] = jp;
}
}
dp[1-cur].push(enCode(),num);
}
}
else if(code[j]^code[j+1])
{
int ma = max(code[j],code[j+1]);
if(mp[i+1][j])
{
code[j] = ma;
code[j+1] = 0;
dp[1-cur].push(enCode(),num);
}
if(mp[i][j+1])
{
code[j] = 0;
code[j+1] = ma;
dp[1-cur].push(enCode(),num);
}
}
else
{
if(mp[i+1][j] && mp[i][j+1])
{
code[j] = 9;
code[j+1] = 9;
dp[1-cur].push(enCode(),num);
}
}
}
dp[cur]._init();
cur = 1^cur;
}
for(int i = 0;i<dp[cur].hs._size;i++)
{
dp[1-cur].push(dp[cur].hs.dat[i]>>3,dp[cur].val[i]);
}
dp[cur]._init();
cur = 1^cur;
/* if(i==1 && j==0)
{
cout << dp[cur].hs._size<< " ";
cout << dp[cur].val[dp[cur].]
}*/
}
printf("%lld\n",ans);
}
return 0;
}