uva 519 - Puzzle (II)

最近在练搜索,这是一道经典的题目。

ac过程:

    dfs迅速写成,关键在于剪枝。

此处用到了预判断和判重两种剪枝中重要的思想

    预判断:1、凹凸的个数完全相同。

                2、边数目的判断(至少能保证能组成矩形的四条边吧)

                3、角的判断(矩形的4个角,我还没来得及写就得到了ac)

(目的是去除一些浪费时间的极端情况)

    判重:在相同位置相同的piece只使用一次

   (目的是减少dfs尝试的次数)

代码:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char piece[100][5];
int n,m,N,inx[100];
int h,L[100],R[100];
bool conjoin(char a, char b)
{
return (a=='F'&&b=='F')||(a+b=='O'+'I');
}
void remove(int k)
{
R[L[k]]=L[R[k]]=k;
}
bool add(int cur,int k)
{
int x=cur/m,y=cur%m;
if(x==n-1)
{
if(piece[k][2]!='F')
return false;
}
if(y==m-1)
{
if(piece[k][1]!='F')
return false;
}
if(x==0)
{
if(piece[k][0]!='F')
return false;
}
else
{
int t=inx[cur-m];
if(!conjoin(piece[k][0],piece[t][2]))
return false;
}
if(y==0)
{
if(piece[k][3]!='F')
return false;
}
else
{
int t=inx[cur-1];
if(!conjoin(piece[k][3],piece[t][1]))
return false;
}
inx[cur]=k;
L[R[k]]=L[k];
R[L[k]]=R[k];
return true;
}
bool dfs(int cur)
{
if(cur==N)
return true;
int count=0;
int top=0,invalid[36];

for(int i=R[h];count<N-cur;count++,i=R[i])
{
bool ok=true;
for(int j=0;j<top;j++)
if(strcmp(piece[i],piece[invalid[j]])==0)
{
ok=false;
break;
}
if(ok&&add(cur,i))
{
h=R[i];
if(dfs(cur+1))
return true;
remove(i);
invalid[top++]=i;
}
}
return false;
}
bool PreJudge()
{
int a[4]={0},b[4]={0},c[4]={0};
for(int i=0;i<N;i++)
{
for(int j=0;j<4;j++)
if(piece[i][j]=='I')
a[j]++;
else if(piece[i][j]=='O')
b[j]++;
else
c[j]++;
}
if(a[0]!=b[2]||a[2]!=b[0]) return false;
if(a[1]!=b[3]||a[3]!=b[1]) return false;
if(a[0]>(n-1)*m) return false;
if(a[2]>(n-1)*m) return false;
if(b[1]>(m-1)*n) return false;
if(b[3]>(m-1)*n) return false;
if(c[0]<m||c[1]<n||c[2]<m||c[3]<n) return false;
return true;
}
int main()
{
while(scanf("%d%d",&n,&m),n+m)
{
N=n*m;
getchar();
for(int i=0;i<N;i++)
L[i]=i-1,R[i]=i+1;
L[0]=N-1,R[N-1]=0;
h=0;
for(int i=0;i<N;i++)
gets(piece[i]);
if(PreJudge()&&dfs(0))
puts("YES");
else
puts("NO");
}
return 0;
}

 

你可能感兴趣的:(uva)