luogu P2346 四子连棋
原题链接:https://www.luogu.org/problem/show?pid=2346
第一次写搜索一遍过,纪念一下先。
论结构体的妙用,直接传一个数组简直太方便。用一个数组记录当前的局面,2为白色,1为黑色,0为空格,已走的步数,下一步那一方走,1为白色,-1为黑色。
然后把初始局面的两种走法都加进队列中,并标记,这里一定要取模,3^16有四千多万,bool数组也不好受
当找到一个空白的格子时,就试试能否移动,如果移动后能形成一个新局面,就加进队列,加进队列之前可以先特判一下是否能结束。虽然是广搜,但是还是要把局面恢复。
#include<cstdio> #include<cstdlib> #include<queue> const int c=1000007; using namespace std; char s[10]; struct mat { int c[5][5],nxt,s; }stb,stw; bool b[2000005][3]; int sum; int nx[5]={0,0,1,0,-1}; int ny[5]={0,1,0,-1,0}; queue<mat>q; int vis(mat x,int ns) { sum=0; for(int i=4;i>=1;i--) { for(int j=4;j>=1;j--) sum=sum*3+x.c[i][j]; } sum%=c; if(b[sum][ns+1]==1) return 1; else { b[sum][ns+1]=1; return 0; } } int win(mat x) { for(int i=1;i<=4;i++) if(x.c[i][1]==x.c[i][2]&&x.c[i][1]==x.c[i][3]&&x.c[i][1]==x.c[i][4]) return 1; for(int i=1;i<=4;i++) if(x.c[1][i]==x.c[2][i]&&x.c[1][i]==x.c[3][i]&&x.c[1][i]==x.c[4][i]) return 1; if(x.c[1][1]==x.c[2][2]&&x.c[1][1]==x.c[3][3]&&x.c[1][1]==x.c[4][4]) return 1; if(x.c[1][4]==x.c[2][3]&&x.c[1][4]==x.c[3][2]&&x.c[1][4]==x.c[4][1]) return 1; return 0; } void move(mat g,int x,int y,int ns) { mat t=g;t.nxt=ns;t.s=g.s+1; for(int i=1;i<=4;i++) { int tx=x+nx[i],ty=y+ny[i]; if(tx<1||tx>4||ty<1||ty>4) continue; if((ns==1&&t.c[tx][ty]==2)||(ns==-1&&t.c[tx][ty]==1)) { t.c[x][y]=t.c[tx][ty]; t.c[tx][ty]=0; if(win(t)==1) { printf("%d",t.s); exit(0); } if(vis(t,ns)==0) q.push(t); t.c[tx][ty]=t.c[x][y]; t.c[x][y]=0; } } } void bfs() { q.push(stb);q.push(stw); while(!q.empty()) { mat t=q.front();q.pop(); for(int i=1;i<=4;i++) { for(int j=1;j<=4;j++) { if(t.c[i][j]==0) move(t,i,j,-t.nxt); } } } } int main() { for(int i=1;i<=4;i++) { scanf("%s",s); for(int j=1;j<=4;j++) { if(s[j-1]=='O') { stb.c[i][j]=0; stw.c[i][j]=0; } if(s[j-1]=='W') { stb.c[i][j]=2; stw.c[i][j]=2; } if(s[j-1]=='B') { stb.c[i][j]=1; stw.c[i][j]=1; } } } stb.nxt=-1; stw.nxt=1; vis(stw,1);vis(stb,-1); bfs(); return 0; }