FZU 1977 Pandora adventure 解题报告(插头DP)

    寻找经过所有必须经过的点的哈密顿回路数。与Ural 1519类似,在求解答案时加入一个判断:当前是否走过了所有的必须经过的点,是否形成回路。

    插头DP入门可看上一篇博客:http://blog.csdn.net/sf____/article/details/15026397

    括号序列代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
const int maxn=100013;

struct Node
{
	int H[maxn];
	LL N[maxn];
	int S[maxn];
	int size;
	void init()
	{
		size=0;
		memset(H,-1,sizeof(H));
	}

	void push(int SS,LL num)
	{
		int s=SS%maxn;
		while( ~H[s] && S[H[s]]!=SS )
			s=(s+1)%maxn;
		if( ~H[s] )
		{
			N[H[s]]+=num;
			return ;
		}
		S[size]=SS;
		N[size]=num;
		H[s]=size++;
	}
} dp[2];
int now,pre;
int maze[14][14];

int get(int S,int p,int l=2)
{
	return (S>>(p*l))&((1<<l)-1);
}

void set(int &S,int p,int v,int l=2)
{
	S^=get(S,p,l)<<(p*l);
	S^=(v&((1<<l)-1))<<(p*l);
}

int main()
{
	int cas=1;
	int n,m;
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		LL ans=0;
		int endx,endy;
		memset(maze,0,sizeof(maze));
		for(int i=0;i<n;i++)
		{
			char str[20];
			scanf("%s",str);
			for(int j=0;j<m;j++)
			{
				switch(str[j])
				{
				case 'X':
					maze[i][j]=0;
					break;
				case 'O':
					endx=i;
					endy=j;
					maze[i][j]=1;
					break;
				case '*':
					maze[i][j]=-1;
					break;
				}
			}
		}

		now=1;
		pre=0;
		dp[now].init();
		dp[now].push(0,1);

		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				swap(now,pre);
				dp[now].init();

				for(int s=0;s<dp[pre].size;s++)
				{
					int S=dp[pre].S[s];
					LL num=dp[pre].N[s];
					int p=get(S,j);
					int q=get(S,j+1);

					if(maze[i][j]==0)
					{
						if(p==0 && q==0)
							dp[now].push(S,num);
						continue;
					}

					if(p==0 && q==0)
					{
						if(maze[i][j]==-1)
							dp[now].push(S,num);

						if(maze[i][j+1] && maze[i+1][j])
						{
							set(S,j,1);
							set(S,j+1,2);
							dp[now].push(S,num);
						}
					}
					else if(p==2 && q==1)
					{
						set(S,j,0);
						set(S,j+1,0);
						dp[now].push(S,num);
					}
					else if( (p>0)^(q>0) )
					{
						if(maze[i+(p>0)][j+(q>0)])
							dp[now].push(S,num);
						set(S,j,q);
						set(S,j+1,p);
						if(maze[i+(q>0)][j+(p>0)])
							dp[now].push(S,num);
					}
					else if( p==1 && q==1 )
					{
						int find=1;
						for(int k=j+2;k<=m;k++)
						{
							int v=get(S,k);
							if(v==2)
								find--;
							else if(v==1)
								find++;

							if(find==0)
							{
								set(S,j,0);
								set(S,j+1,0);
								set(S,k,1);
								dp[now].push(S,num);
								break;
							}
						}
					}
					else if( p==2 && q==2 )
					{
						int find=1;
						for(int k=j-1;k>=0;k--)
						{
							int v=get(S,k);
							if(v==1)
								find--;
							else if(v==2)
								find++;

							if(find==0)
							{
								set(S,j,0);
								set(S,j+1,0);
								set(S,k,2);
								dp[now].push(S,num);
								break;
							}
						}
					}
					else if( p==1 && q==2 )
					{
						set(S,j,0);
						set(S,j+1,0);
						if(S==0 && (i>endx || (i==endx && j>=endy)))
							ans+=num;
					}
				}
			}

			for(int s=0;s<dp[now].size;s++)
				dp[now].S[s]<<=2;
		}

		printf("Case %d: %I64d\n",cas++,ans);
	}
}


    最小表示法代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;

const int maxn=59999;
const int L=3;
int now,pre;
int n,m;
LL ans;
int endx,endy;
int maze[15][15];
int code[15],ch[15];
struct Node
{
	int h[maxn];
	LL n[maxn];
	LL s[maxn];
	int size;
	void init()
	{
		memset(h,-1,sizeof(h));
		size=0;
	}

	void push(LL ss,LL num)
	{
		int i=ss%maxn;
		while( ~h[i] && s[h[i]]!=ss )
			i=(i+1)%maxn;
		if( ~h[i] )
		{
			n[h[i]]+=num;
		}
		else
		{
			s[size]=ss;
			n[size]=num;
			h[i]=size++;
		}
	}
} dp[2];

void decode(LL s)
{
	for(int i=0;i<=m;i++)
		code[i]=s&((1<<L)-1),s>>=L;
}

LL encode()
{
	memset(ch,-1,sizeof(ch));
	ch[0]=0;
	LL s=0;
	int cnt=1;

	for(int i=m;i>=0;i--)
	{
		if( ch[code[i]]==-1 ) ch[code[i]]=cnt++;
		s<<=L;
		s|=ch[code[i]];
	}
	return s;
}

void shift()
{
	for(int s=0;s<dp[now].size;s++)
		dp[now].s[s]<<=L;
}

void merge(int a,int b)
{
	for(int i=0;i<=m;i++) if(code[i]==a)
		code[i]=b;
}

void doGrid(int i,int j)
{
	for(int s=0;s<dp[pre].size;s++)
	{
		decode(dp[pre].s[s]);
		LL n=dp[pre].n[s];
		int left=code[j];
		int up=code[j+1];
		int ma=max(left,up);
		int mi=min(left,up);

		if(maze[i][j]==0)
		{
			if(ma==0)
				dp[now].push(encode(),n);
			continue;
		}

		if(ma==0)
		{
			if(maze[i][j]==1)
			{
				dp[now].push(encode(),n);
			}
			if(maze[i][j+1] && maze[i+1][j])
			{
				code[j]=code[j+1]=13;
				dp[now].push(encode(),n);
			}
		}
		else if(mi==0)
		{
			if(maze[i+1][j])
			{
				code[j]=ma;
				code[j+1]=0;
				dp[now].push(encode(),n);
			}
			if(maze[i][j+1])
			{
				code[j]=0;
				code[j+1]=ma;
				dp[now].push(encode(),n);
			}
		}
		else if(left==up)
		{
			if(i>endx || (i==endx && j>=endy))
			{
				code[j]=0;
				code[j+1]=0;
				if(encode()==0) ans+=n;
			}
		}
		else
		{
			code[j]=code[j+1]=0;
			merge(left,up);
			dp[now].push(encode(),n);
		}
	}
}

void solve()
{
	now=1,pre=0;
	ans=0;
	dp[now].init();
	dp[now].push(0,1);

	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			swap(now,pre);
			dp[now].init();
			doGrid(i,j);
		}
		shift();
	}
}

void init()
{
	memset(maze,0,sizeof(maze));
	endx=-1;
	char str[20];
	for(int i=0;i<n;i++)
	{
		scanf("%s",str);
		for(int j=0;j<m;j++)
		{
			if(str[j]=='O')
				maze[i][j]=2,endx=i,endy=j;
			else if(str[j]=='*')
				maze[i][j]=1;
		}
	}
}

int main()
{
	int T;
	int cas=1;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		init();
		solve();
		printf("Case %d: %I64d\n",cas++,ans);
	}
}



你可能感兴趣的:(dp,插头DP)