【Codeforces 1205C】Palindromic Paths(回文路径DP)

题目链接

对于格子 ( i , j ) (i,j) (i,j),若 i + j i+j i+j为奇数则称为奇点,否则称为偶点。
 
首先发现询问距离为2的两个格子就可以知道它们是否值相同。已知 ( 1 , 1 ) (1,1) (1,1)格子值为1, ( n , n ) (n,n) (n,n)格子值为0,一遍BFS可以求出所有偶点的值。
 
如果 ( 1 , 2 ) (1,2) (1,2)格子的值已知,那么再一遍BFS可以求出所有奇点的值。我们假设 ( 1 , 2 ) (1,2) (1,2)格子值为0/1,得到两种结果。因题目保证有解,所以两种结果下必然存在一组合法询问的答案不同,我们得到这组询问的答案就知道哪种结果正确了。
 
我们用DP分别求出两种结果下所有询问的答案即可。设 S [ x 1 ] [ y 1 ] [ x 2 ] [ y 2 ] S[x1][y1][x2][y2] S[x1][y1][x2][y2]表示 ( x 1 , y 1 ) (x1,y1) (x1,y1) ( x 2 , y 2 ) (x2,y2) (x2,y2)之间是否有合法回文路径,它为YES的条件是: ( x 1 , y 1 ) (x1,y1) (x1,y1) ( x 2 , y 2 ) (x2,y2) (x2,y2)两个格子的值相同,且它们之间存在合法路径,并满足: ( x 1 , y 1 ) (x1,y1) (x1,y1) ( x 2 , y 2 ) (x2,y2) (x2,y2)相同或接壤,或者存在 ( x 1 ′ , y 1 ′ ) (x1',y1') (x1,y1) ( x 2 ′ , y 2 ′ ) (x2',y2') (x2,y2),满足 ( x 1 ′ , y 1 ′ ) (x1',y1') (x1,y1) ( x 1 , y 1 ) (x1,y1) (x1,y1)路径的后继, ( x 2 ′ , y 2 ′ ) (x2',y2') (x2,y2) ( x 2 , y 2 ) (x2,y2) (x2,y2)路径的前驱,且 S [ x 1 ′ ] [ y 1 ′ ] [ x 2 ′ ] [ y 2 ′ ] S[x1'][y1'][x2'][y2'] S[x1][y1][x2][y2]为YES。用记忆化搜索写起来会方便些,复杂度为 O ( n 4 ) O(n^4) O(n4)
 
在两遍BFS中一共用到了 n 2 − 3 n^2-3 n23个询问,最后用到了一个询问,询问总数为 n 2 − 2 n^2-2 n22,符合题意。

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<string.h>
#include<set>
#define LL long long
using namespace std;
LL read( )
{
  LL sum=0;char c=getchar( );bool f=0;
  while(c<'0' || c>'9') {if(c=='-') f=1;c=getchar( );}
  while(c>='0' && c<='9') {sum=sum*10+c-'0';c=getchar( );}
  if(f) return -sum;
  return sum;
}
int n;
const int dx[ ]={2,1,1,0,0,-1,-1,-2};
const int dy[ ]={0,1,-1,2,-2,1,-1,0};
const int DX[ ]={0,1,-1,0};
const int DY[ ]={1,0,0,-1};
#define CI const int
bool CH(int &x1,int &y1,int &x2,int &y2)
{
  if(x1<=x2&&y1<=y2) return 1;
  swap(x1,x2);swap(y1,y2);
  return(x1<=x2&&y1<=y2);
}
bool ASK(CI &x1,CI &y1,CI &x2,CI &y2)
{
  printf("? %d %d %d %d\n",x1,y1,x2,y2);
  fflush(stdout);return read( )^1;
} 
int qx[2505],qy[2505];
struct EX
{
  bool S[55][55],vis[55][55];
  bool F[55][55][55][55],V[55][55][55][55];
  void BFS(bool T)
  {
    int h,t,i,x,y,x1,y1,x2,y2,rx,ry;
    if(!T) {h=1;t=2;qx[1]=1;qy[1]=1;S[1][1]=1;vis[1][1]=1;qx[2]=n;qy[2]=n;S[n][n]=0;vis[n][n]=1;}
    else {h=t=1;qx[1]=1;qy[1]=2;S[1][2]=0;vis[1][2]=1;}
    while(h<=t)
      {
		x=qx[h];y=qy[h];h++;
		for(i=0;i<8;i++)
		  {
		    x1=x;y1=y;x2=x1+dx[i];y2=y1+dy[i];
		    if(x2<1||x2>n||y2<1||y2>n||vis[x2][y2]) continue;
		    if(CH(x1,y1,x2,y2))
		      {
				if(x==x2&&y==y2) rx=x1,ry=y1;else rx=x2,ry=y2;
				vis[rx][ry]=1,S[rx][ry]=S[x][y]^ASK(x1,y1,x2,y2),qx[++t]=rx,qy[t]=ry;
		      }
		  }
      }
  }
  bool DFS(int x1,int y1,int x2,int y2)
  {
    if(V[x1][y1][x2][y2]) return F[x1][y1][x2][y2];
    V[x1][y1][x2][y2]=1;
    if(S[x1][y1]!=S[x2][y2]) return F[x1][y1][x2][y2]=0;
    if(abs(x1-x2)+abs(y1-y2)<=1) return F[x1][y1][x2][y2]=1;
    int i,j,X1,Y1,X2,Y2;
    for(i=0;i<2;i++)
      {
		X1=x1+DX[i];Y1=y1+DY[i];
		if(X1<1||X1>n||Y1<1||Y1>n) continue;
		for(j=2;j<4;j++)
		  {
		    X2=x2+DX[j];Y2=y2+DY[j];
		    if(X2>=1&&X2<=n&&Y1>=1&&Y2<=n&&CH(X1,Y1,X2,Y2)&&DFS(X1,Y1,X2,Y2)) return F[x1][y1][x2][y2]=1;
		  }
      }
    return F[x1][y1][x2][y2]=0;
  }
  void output( )
  {
    puts("!");
    for(int i=1;i<=n;i++)
      {
		for(int j=1;j<=n;j++) printf("%d",S[i][j]);
		puts("");
      }
  }
}K1,K2;
int main( )
{
  n=read( );
  K1.BFS(0);K1.BFS(1);
  int i,j,k,t,x1,y1,x2,y2;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      K2.S[i][j]=K1.S[i][j]^((i+j)&1);
  for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
      for(k=1;k<=n;k++)
		for(t=1;t<=n;t++)
		  {
		    x1=i;y1=j;x2=k;y2=t;
		    if(!CH(x1,y1,x2,y2)) continue;
		    K1.DFS(x1,y1,x2,y2),K2.DFS(x1,y1,x2,y2);
		    if(K1.F[x1][y1][x2][y2]==K2.F[x1][y1][x2][y2]) continue;
		    if(abs(x1-x2)+abs(y1-y2)==1) continue;
		    if(ASK(x1,y1,x2,y2)==K1.F[x1][y1][x2][y2]) K2.output( );
		    else K1.output( );
		    return 0;
		  }
  return 0;
}
  
	

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值