【2017NOIP普及组】T3 棋盘

P3956 棋盘
题目传送门
难点:逻辑要紧密,千万不要漏判!
思路(dfs):
按题意分情况深搜。
注意几点:

  1. 数据太大,我们考虑记忆化剪枝
  2. 当前点无色,需染色,深搜后还需回溯啦
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e3+5;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int map[N][N],f[N][N],m,n,ans=MAX;
bool flag;
void input()
{
	memset(f,127/3,sizeof(f));
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++)
	{
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		map[x][y]=c+1;
	}
}
bool check(int x,int y) {return (x<1||y<1||x>m||y>m)?0:1;}
void dfs(int x,int y,int cost,bool flag)
{
	if(cost>=f[x][y]) return; //剪枝 
	if(x==m&&y==m) {ans=min(cost,ans); return;}
	f[x][y]=cost;  //记忆化 
    for(int i=0;i<4;i++)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if(check(nx,ny))
		{
			if(map[nx][ny]) //有色 
        	{
        		if(map[x][y]==map[nx][ny]) dfs(nx,ny,cost,0);//同色
        		else  dfs(nx,ny,cost+1,0);//不同色
			}
			else //无色 
				if(!flag)
				{
					map[nx][ny]=map[x][y];
					dfs(nx,ny,cost+2,1);
					map[nx][ny]=0;
				}	
		} 
	}
}
int main()
{
	//fre();
	input();
	dfs(1,1,0,0);
	printf("%d",ans==MAX?-1:ans);
	return 0;
}

思路(bfs):
此题是求最优解,我自然就想到了bfs做,但实际上bfs恐怖得难以想象(有很多坑)。
反而dfs秒过,只需记忆化克服这毒瘤数据。

坑1:
对于每个点都有多个状态,所以我们不能确定就是最优解,所以我才用了一个奇葩的方法,类似最短路,除外,我还用了一句玄学代码去更新最优状态: int jb=min(dis[x][y],que[head].b);,其实我实在不会网上大佬的方法。

坑2:
如果当前点无色,在前面也提到了,需要染色。但是又不能像dfs一样回溯,我便想了一个方法,将染色值=原色+2。这样做真得很巧妙,既不影响我们判断它的原色,又能记录它被染的颜色,俗话说:“柳暗花明又一村”,我也是在快抓破头皮时想到的。

加入队列的点属性各异,我……,自己看程序吧!

推荐大家看看我的另一篇博客机器人搬重物
你会对bfs有新的认识的,大佬除外。

其他都是渣啊~

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=707406378;
const int N=1e3+10;
const int dx[]={0,-1,1,0,0};
const int dy[]={0,0,0,-1,1};
struct node
{
	int x,y,mo,b;
} que[N*N]; 
int m,n,dis[N][N],map[N][N];
bool vis[N][N];
void input()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++)
	{
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		map[x][y]=c+1;
		//黄色 2;红色 1;无色 0 
	}
}
bool check(int x,int y)
{
	if(x>=1&&x<=m&&y>=1&&y<=m) return 1;
	return 0;
}
void bfs()
{
	memset(dis,127/3,sizeof(dis));
	vis[1][1]=1;
	dis[1][1]=0;
	que[1].x=que[1].y=1;
	int head=0,tail=1;
	while(head<=tail)
	{
		head++;
		int x=que[head].x,y=que[head].y,mf=que[head].mo;
		int jb=min(dis[x][y],que[head].b);//取最优解 
		for(int i=1;i<=4;i++)
		{
			int flag=0;
			int nx=x+dx[i],ny=y+dy[i];
			if(check(nx,ny))
			{
				if(mf)  //已使用魔法 
				{
					if(!map[nx][ny]||map[nx][ny]>2) continue;  //无色点 
					if(map[x][y]-2==map[nx][ny]&&dis[nx][ny]>jb)  //同色 
						dis[nx][ny]=jb,flag=1;
					else if(map[x][y]-2!=map[nx][ny]&&dis[nx][ny]>jb+1) //不同色 
						dis[nx][ny]=jb+1,flag=2;	
				}	
				else  //未使用魔法
				{
					if(map[nx][ny]==1||map[nx][ny]==2)	//下一个点本来有颜色 
					{
						if(map[x][y]==map[nx][ny]&&dis[nx][ny]>jb) //同色 
							dis[nx][ny]=jb,flag=1;
						if(map[x][y]!=map[nx][ny]&&dis[nx][ny]>jb+1) //不同色 
							dis[nx][ny]=jb+1,flag=2;	
					}
					if(!map[nx][ny]||map[nx][ny]>2&&dis[nx][ny]>jb+2) //下个点无色 
						dis[nx][ny]=jb+2,flag=3,map[nx][ny]=map[x][y]+2;
						//染色	
				}
				if(!vis[nx][ny]) 
				{
					vis[nx][ny]=1;
					tail++;
					que[tail].x=nx,que[tail].y=ny,que[tail].mo=0,que[tail].b=jb;
					if(flag==2) que[tail].b+=1;
					if(flag==3) que[tail].mo=1,que[tail].b+=2;	
				}
			}
		}
	}
}
int main()
{
	//fre();
	input();
	bfs(); 
	printf("%d",dis[m][m]==MAX?-1:dis[m][m]);
	return 0;
}

原创文章 157 获赞 148 访问量 8304

猜你喜欢

转载自blog.csdn.net/bigwinner888/article/details/105871368