P3956 棋盘

最近在做广搜的题,一场六六欢乐赛彻底让我意识到了暴搜的重要性 所以我为什么要去做广搜。想着先把广搜的黄题刷完(听同学说广搜的橙题更难),结果这一道题我就调了大概4个小时,还是写一篇博客吧

拿到这道题,最开始是没啥思路的,因为这种题型其实是没怎么遇见过的,之前做过的大多广搜题都是基本上直接套模板,多做这种题加深理解和锻炼思维吧

最开始的思路还是先用DFS暴搜一下。函数中放四个参数,前两个参数为最基础的坐标,第三个为目前的花费,第四个记录当前是否使用魔法:对于相同颜色的情况,花费不变;对于颜色不同的情况(有颜色),花费+1;对于空白情况,花费+2,并把下一个位置涂上色(最开始没加这个调了很久)。这样的话就得到了第一个程序(60分)

#include<bits/stdc++.h>
using namespace std;
int m,n;
int a[105][105];
bool b[105][105];
int ans=20040915;    //初始化为妹子的生日(玄学优化)
int mx[4]={1,0,0,-1};
int my[4]={0,-1,1,0};
void dfs(int x,int y,int sum,bool op){
	if(sum>ans) return;
	if(x==m&&y==m){
		ans=min(ans,sum);
		return;
	}
	for(register int i=0;i<4;i++){
		int nx=x+mx[i];
		int ny=y+my[i];
		if(nx>=1&&nx<=m&&ny>=1&&ny<=m&&b[nx][ny]==false){
			if(a[nx][ny]==a[x][y]){
				b[nx][ny]=true;
				dfs(nx,ny,sum,false);
				b[nx][ny]=false;
			}else if(a[nx][ny]==0&&op==false){
				b[nx][ny]=true;
				a[nx][ny]=a[x][y];
				dfs(nx,ny,sum+2,true);
				a[nx][ny]=0;
				b[nx][ny]=false;
			}else if(a[nx][ny]!=a[x][y]&&a[nx][ny]){
				b[nx][ny]=true;
				dfs(nx,ny,sum+1,false);
				b[nx][ny]=false;
			}
		}
	}
}
int main(){
	scanf("%d%d",&m,&n);
	for(register int i=1;i<=n;i++){
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		a[x][y]=c+1;
	}
	b[1][1]=true;
	dfs(1,1,0,false);
	if(ans==20040915){
		cout<<-1;
	}else{
		cout<<ans;
	}
	return 0;
}

然后继续想,发现有个记忆化搜索这种东西,开一个f数组,对于搜索的当前坐标,记录到此坐标的最小化费,对于之后大于它的花费,直接return,最后的答案就是 f [ m ][ m ](70分)

#include<bits/stdc++.h>
using namespace std;
int m,n;
int a[105][105];
bool b[105][105];
int mx[4]={1,0,0,-1};
int my[4]={0,-1,1,0};
int f[105][105];
inline void dfs(int x,int y,int sum,bool op){
	if(sum>f[x][y]) return;
	f[x][y]=sum;
	for(register int i=0;i<4;i++){
		int nx=x+mx[i];
		int ny=y+my[i];
		if(nx>=1&&nx<=m&&ny>=1&&ny<=m&&b[nx][ny]==false){
			if(a[nx][ny]==a[x][y]){
				b[nx][ny]=true;
				dfs(nx,ny,sum,false);
				b[nx][ny]=false;
			}else if(a[nx][ny]==0&&op==false){
				b[nx][ny]=true;
				a[nx][ny]=a[x][y];
				dfs(nx,ny,sum+2,true);
				a[nx][ny]=0;
				b[nx][ny]=false;
			}else if(a[nx][ny]!=a[x][y]&&a[nx][ny]){
				b[nx][ny]=true;
				dfs(nx,ny,sum+1,false);
				b[nx][ny]=false;
			}
		}
	}
	return ;
}
int main(){
	scanf("%d%d",&m,&n);
	for(register int i=1;i<=m;i++) fill(f[i]+1,f[i]+1+m,20040915);
	for(register int i=1;i<=n;i++){
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		a[x][y]=c+1;
	}
	b[1][1]=true;
	dfs(1,1,0,false);
	if(f[m][m]==20040915){
		puts("-1");
	}else{
		printf("%d",f[m][m]);
	}
	return 0;
}

请教了旁边zjy的大佬,他用的是广搜,但是这道题我自己编的广搜连样例都不了(菜),最后选择看了题解,发现对于每次答案的判断应该放在判断越界之后的if语句中,放在外面的话起到的优化作用其实并不大。然后恭喜你,有95分了,我也不知道为什么会第一个数据WA掉,下载了数据之后,特判了一下m=1的情况,直接输出0,这样你就成功AC了

#include<bits/stdc++.h>
using namespace std;
int m,n;
int a[105][105];
bool b[105][105];
int mx[4]={-1,0,1,0};
int my[4]={0,-1,0,1};
int f[105][105];
inline void dfs(int x,int y,int sum,bool op){
	if(sum>f[x][y]) return;
	for(register int i=0;i<4;i++){
		int nx=x+mx[i];
		int ny=y+my[i];
		if(sum>f[x][y]) return ;
		if(nx>=1&&nx<=m&&ny>=1&&ny<=m&&b[nx][ny]==false){
			if(a[nx][ny]==a[x][y]&&sum<f[nx][ny]){    //之后的判断要写<,别写成<=,会增加时间复杂度 
				b[nx][ny]=true;
				f[nx][ny]=sum;
				dfs(nx,ny,sum,false);
				b[nx][ny]=false;
			}else if(a[nx][ny]==0&&op==false&&sum+2<f[nx][ny]){
				b[nx][ny]=true;
				a[nx][ny]=a[x][y];
				f[nx][ny]=sum+2;
				dfs(nx,ny,sum+2,true);
				a[nx][ny]=0;
				b[nx][ny]=false;
			}else if(a[nx][ny]!=a[x][y]&&a[nx][ny]&&sum+1<f[nx][ny]){
				b[nx][ny]=true;
				f[nx][ny]=sum+1;
				dfs(nx,ny,sum+1,false);
				b[nx][ny]=false;
			}
		}
	}
	return ;
}
signed main(){
	scanf("%d%d",&m,&n);
	if(m==1){
		cout<<0;
		return 0;
	}
	for(register int i=1;i<=m;i++) fill(f[i]+1,f[i]+1+m,20040915);
	for(register int i=1;i<=n;i++){
		int x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		a[x][y]=c+1;
	}
	b[1][1]=true;
	dfs(1,1,0,false);
	
	if(f[m][m]==20040915){
		puts("-1");
	}else{
		printf("%d",f[m][m]);
	}
	return 0;
}
posted @ 2020-06-11 19:28  Poetic_Rain  阅读(106)  评论(0编辑  收藏  举报