您的位置:首页 > 其它

Ural 1519 Formula 1

2013-03-04 19:13 267 查看
题意:找出N*M大小的带障碍格子的哈密顿回路总数。

题解:插头DP进阶题,也是入门题,插头DP的详解还是看《基于连通性状态压缩的动态规划问题》论文吧。

12列共13个插头,一共有3种插头,3进制可解,但用位操作处理2进制更容易,所以设成4进制,2位代表1个插头的状态,用括号表示法即 00:无插头;01:'(';10:‘)’,26位长度的状态是开不了的,所以用hash表进行优化处理,我用一个head指针指向表中任意一个,便于遍历整个hash表。

对每个格子的情况我进行了下面分类。

首先,如果该格子有障碍,那么只能是没有插头的状态才能继续向下转移。

反之,如果只有一个插头,那么也比较容易处理,直接转移成两种状态即可。较为麻烦的是有两个插头,这涉及到合并操作,由于第一次写,所以分了四小类(本质上只有两类):

1、左插头与上插头属于同一条路径,即括号状态中两个插头对应的括号是互相匹配的,又由于左插头与上插头是轮廓线相邻两个,那么一旦合并,意味着形成回路,也就是只有最右下角的非障碍格子才有可能(进行一次特判)。另外,这里判断两括号是否匹配也无需搜索,只需左插头是左括号,上插头是右括号即可(因为路径不交叉)。

2、如果左插头是右括号,上插头是左括号,连接后两括号消失,原来各自代表的路径连成一条,且原左插头的右括号所对应的左括号仍旧是新路径的左括号。

3、如果左插头与上插头均是左括号,那么连接后,会使得原来左插头所代表的左括号右移至上插头所代表的左括号匹配的右括号上,只需将该右括号改为左括号,即在右括号的位置异或上11B即可。

4、如果均为右括号,那么和3一样,不同的只是将上插头所代表的右括号移动到左插头对应的左括号去而已。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX=100007;
typedef long long LL;
struct HT
{
int head,hash[MAX],nxt[MAX];
LL cnt[MAX];
void init()
{
head=-1;
memset(hash,-1,sizeof(hash));
memset(nxt,-1,sizeof(nxt));
memset(cnt,0,sizeof(cnt));
}
void insert(int st,LL ct)
{
LL pos=st%MAX;
LL ad=1;
while(hash[pos]!=st&&hash[pos]!=-1)
{
pos+=ad*ad;
ad++;
if(pos>=MAX)pos%=MAX;
}
if(hash[pos]==-1)
{
hash[pos]=st;
cnt[pos]=ct;
nxt[pos]=head;
head=pos;
}
else
cnt[pos]+=ct;
}
} dp[2];
int n,m;
char mp[14][14];
int findright(int st,int pos)
{
int tp,p=pos;
for(int dep=1; dep!=0; p+=1)
{
tp=((st>>((m-p+1)*2))&3)%4;
switch(tp)
{
case 0:
break;
case 1:
dep++;
break;
case 2:
dep--;
break;
}
if(dep==0)
break;
}
return 3<<((m-p+1)*2);
}
int findleft(int st,int pos)
{
int tp,p=pos;
for(int dep=1; dep!=0; p-=1)
{
tp=((st>>((m-p+1)*2))&3)%4;
switch(tp)
{
case 0:
break;
case 1:
dep--;
break;
case 2:
dep++;
break;
}
if(dep==0)
break;
}
return 3<<((m-p+1)*2);
}
LL getans(int fg)
{
LL pos=0,ad=1;
while(dp[fg].hash[pos]!=0&&dp[fg].hash[pos]!=-1)
{
pos+=ad*ad;
ad++;
if(pos>=MAX)pos%=MAX;
}
if(dp[fg].hash[pos]==-1)
{
return 0ll;
}
else
return dp[fg].cnt[pos];
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1; i<=n; i++)scanf(" %s",mp[i]+1);
int fg1=1,fg2,ex,ey;
for(ex=n; ex>=1; ex--)
{
for(ey=m; ey>=1; ey--)
{
if(mp[ex][ey]=='.')
{
fg1=0;
break;
}
}
if(!fg1)
break;
}
if(fg1)
{
printf("0\n");
continue;
}
fg1=0,fg2=1;
dp[fg2].init();
dp[fg2].insert(0,1ll);
for(int i=1; i<=ex; i++)
{
fg1=!fg1;
fg2=!fg2;
dp[fg2].init();
for(int tp=dp[fg1].head; tp!=-1; tp=dp[fg1].nxt[tp])
{
if(dp[fg1].hash[tp]&3)
continue;
dp[fg2].insert(dp[fg1].hash[tp]>>2,dp[fg1].cnt[tp]);
}
for(int j=0; j<m; j++)
{
fg1=!fg1;
fg2=!fg2;
dp[fg2].init();
int a=3<<(2*(m-j)),b=3<<(2*(m-j-1));
for(int pos=dp[fg1].head; pos!=-1; pos=dp[fg1].nxt[pos])
{
int st=dp[fg1].hash[pos];
LL ct=dp[fg1].cnt[pos];
int x=((st&a)>>(2*(m-j)))%4,y=((st&b)>>(2*(m-j-1)))%4;
if(mp[i][j+1]=='*')
{
if(x||y)
continue;
dp[fg2].insert(st,ct);
}
else if(x)
{
if(y)
{
if(x==1&&y==1)
dp[fg2].insert((st&(~a)&(~b))^findright(st,j+3),ct);
else if(x==2&&y==1)
dp[fg2].insert(st&(~a)&(~b),ct);
else if(x==2&&y==2)
dp[fg2].insert((st&(~a)&(~b))^findleft(st,j),ct);
else if(x==1&&y==2&&i==ex&&j==ey-1)
dp[fg2].insert(st&(~a)&(~b),ct);
}
else
{
dp[fg2].insert(st,ct);
dp[fg2].insert(st&(~a)|(x<<(2*(m-j-1))),ct);
}
}
else if(y)
{
dp[fg2].insert(st,ct);
dp[fg2].insert(st&(~b)|(y<<(2*(m-j))),ct);
}
else
dp[fg2].insert(st|(1<<(2*(m-j)))|(2<<(2*(m-j-1))),ct);
}
}
}
printf("%lld\n",getans(fg2));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: