某CF的D

刚开始按(wqy)的思路写,提交了(n)遍也没过。。。
学到了压成一维。

还是梳理梳理错因吧:

  • 1.对每个井号进行(bfs),会超时(我真是太傻逼了)。
  • 2.对于(0)的情况,一个点如果之前被标记过,并且之前被标记的和现在要标记的值不一样,答案才会是(0)
  • 3.在初始化的时候也要判断2中的情况(之前被标记过),因为有一行(或者一列)的情况。
  • 4.(wqy)说的四周是周围(8)个格子。
  • 5.对于一个点,如果进队了要标记,防止进队很多次。((test\,10)
  • 6.关于出队清不清标记???((test\,18)

改成出队不清标记,进队是没有标记才进。才同时过了10,18

先贴一份没有(AC)的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 2e6+5;
int n,m,a[N],id[N],qx[N],qy[N],l=1,r;
int dx[10]={0,0,1,-1,0,-1,1,-1,1};
int dy[10]={0,1,0,0,-1,-1,1,1,-1};
bool book[N],flag=0;
char c;
/*int bft(int x,int y)
{
	memset(book,0,sizeof(book));
	qx[1]=x; qy[1]=y;
	l=r=1;
	bool flag1=0,flag2=0;
	if(x==n||y==1) flag1=1;
	if(x==1||y==m) flag2=1;
	while(l<=r)
	{
		if(flag1&&flag2) break;
//	    if(flag3&&t) return 5;
		int xt=qx[l],yt=qy[l],xx,yy;
		++l;
		for(int i=1;i<=8;i++)
		{
//			if(t&&i==3) break;
			xx=xt+dx[i],yy=yt+dy[i];
			if(xx>n||yy>m) continue;
			if(xx<1||yy<1) continue;
			if(a[(xx-1)*m+yy]==0&&!book[(xx-1)*m+yy])
			{
				qx[++r]=xx; qy[r]=yy;
				book[(xx-1)*m+yy]=1;
				if(xx==n||yy==1) flag1=1;
				if(xx==1||yy==m) flag2=1;
//				if(xx==n&&yy==m) flag3=1;
			}
		}
	}
//	if(flag3&&t) return 5;
	if(flag1&&flag2) return 3;
	if(flag1) return 1;
	if(flag2) return 2;
	return 0;
}*/
void bfs()
{
	while(l<=r)
	{
		int xt=qx[l],yt=qy[l],xx,yy;
		++l; 
//		book[(xt-1)*m+yt]=0;
		for(int i=1;i<=8;i++)
		{
			xx=xt+dx[i],yy=yt+dy[i];
			if(xx<1||xx>n) continue;
			if(yy<1||yy>m) continue;
			if(!a[(xx-1)*m+yy])
			{
				if(id[(xx-1)*m+yy]!=0&&id[(xx-1)*m+yy]!=id[(xt-1)*m+yt])
				 { flag=1; return ; }
//				{ flag=1; return ;}
				id[(xx-1)*m+yy]=id[(xt-1)*m+yt];
//				printf("%d %d %d
",r,xx,yy);
				if(!book[(xx-1)*m+yy]) qx[++r]=xx,qy[r]=yy;
				book[(xx-1)*m+yy]=1;
			}
		}
	}
//	cout<<flag<<"*******"<<endl;
//	cout << r <<endl;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 {
	 	cin>>c;
	 	if(c=='.') a[(i-1)*m+j]=1;
	 	else a[(i-1)*m+j]=0;
	 }
	 // 1 左下 2 右上  
	 for(int i=1;i<=n;i++)
	 {
	 	if(a[(i-1)*m+1]==0&&id[(i-1)*m+1]==2) { printf("0
"); return 0; }
	 	if(a[(i-1)*m+1]==0) { id[(i-1)*m+1]=1,qx[++r]=i,qy[r]=1,book[(i-1)*m+1]=1;}
	 	
	 	if(a[(i-1)*m+m]==0&&id[i*m]==1) { printf("0
"); return 0; }
	 	if(a[(i-1)*m+m]==0) { id[i*m]=2,qx[++r]=i,qy[r]=m,book[i*m]=1;}
	 }
	for(int i=1;i<=m;i++)
	{
		if(a[i]==0&&id[i]==1) { printf("0
"); return 0; }
		if(a[i]==0) { id[i]=2,qx[++r]=1,qy[r]=i,book[i]=1;}
		
		if(a[(n-1)*m+i]==0&&id[(n-1)*m+i]==2) { printf("0
"); return 0; }
		if(a[(n-1)*m+i]==0)  { id[(n-1)*m+i]=1,qx[++r]=n,qy[r]=i,book[(n-1)*m+i]=1; }
	}
/*	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		 cout<<id[(i-1)*m+j]<<"*";
		cout<<endl;
	}*/
	bfs();
	if(flag) { printf("0
"); return 0; } 
	bool a1=0,a2=0;
/*	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 if(a[(i-1)*m+j]==0&&!id[(i-1)*m+j])
	 {
	 	a1=0,a2=0;
	 	for(int k=1;k<=8;k++)
	 	{
	 		if(i+dx[k]<=0||i+dx[k]>n) continue;
	 		if(j+dy[k]<=0||j+dy[k]>m) continue;
	 		if(id[(i+dx[k]-1)*m+j+dy[k]]==1) a1=1;
	 		if(id[(i+dx[k]-1)*m+j+dy[k]]==2) a2=1;
	 	}
	 	if(a1==1&&a2==1) { printf("0
"); return 0;}
	 	if(a1==1) id[(i-1)*m+j]=1;
	 	if(a2==1) id[(i-1)*m+j]=2;
	 }*/
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
//	 if(a[(i-1)*m+j]==1)
	 {
	 	if(i==1&&j==1) continue;
	 	if(i==n&&j==m) continue;
	 	a1=0,a2=0;
	    if(i==n||j==1) a1=1;
	    if(i==1||j==m) a2=1;
	 	for(int k=1;k<=8;k++)
	 	{
	 		if(i+dx[k]<=0||i+dx[k]>n) continue;
	 		if(j+dy[k]<=0||j+dy[k]>m) continue;
	 		if(id[(i+dx[k]-1)*m+j+dy[k]]==1) a1=1;
	 		if(id[(i+dx[k]-1)*m+j+dy[k]]==2) a2=1;
	 	}
	 	if(a1&&a2) 
	 	{
	 		if(a[(i-1)*m+j]==0) { printf("0
"); return 0; }
	 		else  { printf("1
"); return 0; }
	 	}
	 }
	printf("2
");
	return 0;
}

(Dyj)太神辣!!!

思路:

先判断0的情况。
对于剩下两种情况:

1.找一条最靠右,或最靠下的路径,把它全部标记上,再在标记后的新矩阵中找是否有从(1,1)(n,m)的路径,如果有,答案为2,如果没有,答案为1.
2.如果答案为1,那么所有可行的路径必然经过同一个点(该标记的点,记为(w)),对于可行路径中,交点最少的两条路径一定是最靠右的一条和最靠下的一条。如果这两条路径有交点,那么答案为1,否则为2。

对1的感性证明:
如果答案为1的话,标记一条路径,那么(w)一定会被标记。但是标记任意一条不行,这样会把应该是2的答案判成1。
为什么呢?
因为标记一整条路径的话,除了(w)点是应该标记的,还会多标记好多点。假如其中某一个点是某一条可以不经过(w)点到(n,m)的路径必须经过的点的话,就会出错。
还是画个样例吧:

enter image description here

如果标记了红色的路径,就会错判为答案是1。
但是标记最右,或最下的路径,就不会出现这种情况。(感性理解吧 )

不得不再说一遍,(Dyj)太强了。
按刁神的思路,我一直写(T)
(dfs)时先向右走,再向下走。既然这样(T)的话,能不能每次只向一个方向走,反正找到一条路径就可以了嘛。
但是,这样不行
原因:如果一个点能向右走,但是向右走几个格之后就没路了,所以在这个格应该向下走,但是已经向右走过了,所以向下的搜不到了。

刁神的神奇做法:
第一遍判情况0时的(bfs)对每个点记录一个相当于(dis)的值,每个点的(dis)都等于从它转移过来的那个点的(dis+1)。然后(dfs)找路径标记的时候,从(n,m)向左上走,假如接下来要搜的点的(dis)等于当前点的(dis-1),那么才搜,否则不搜。
推荐大佬的博客

一样没有(AC)的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1e6+10;
int n,m,a[N],qx[N],qy[N],l=1,r;
bool book[N],flag;
char c;
void work()
{
/*	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		 cout<<book[(i-1)*m+j]<<"*";
		cout<<endl;
	}*/
	book[n*m]=0;
	flag=1;
	bool at=0;
	qx[++r]=1, qy[r]=1;
	while(l<=r)
	{
		int xt=qx[l],yt=qy[l]; ++l;
		if(xt==n&&yt==m) { at=1; printf("2
"); return ; }
		if(xt+1<=n&&!book[xt*m+yt]&&a[xt*m+yt]) 
		 qx[++r]=xt+1,qy[r]=yt,book[xt*m+yt]=1;
		if(yt+1<=m&&!book[(xt-1)*m+yt+1]&&a[(xt-1)*m+yt+1]) 
		 qx[++r]=xt,qy[r]=yt+1,book[(xt-1)*m+yt+1]=1;
	}
	if(!at) printf("1
");
	return ;
}
void dfs(int x,int y)
{
	if(x==n&&y==m) { work(); return ;}
	if(flag) return ;
	if(x>n||x<1) return ;
	if(y<1||y>m) return ;
	if(a[(x-1)*m+y+1]==1&&y+1<=m) 
	 { book[(x-1)*m+y+1]=1; dfs(x,y+1); book[(x-1)*m+y+1]=0;}
	if(a[x*m+y]==1&&x+1<=n) 
	 { book[x*m+y]=1; dfs(x+1,y); book[x*m+y]=0;}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 {
	 	cin>>c;
	 	if(c=='.') a[(i-1)*m+j]=1;
	 }
	book[1]=1;
	dfs(1,1);
	if(flag==0) printf("0
");
	return 0;
}
原文地址:https://www.cnblogs.com/karryW/p/11478326.html