P3956 棋盘
题目传送门
难点:逻辑要紧密,千万不要漏判!
思路(dfs):
按题意分情况深搜。
注意几点:
- 数据太大,我们考虑记忆化剪枝
- 当前点无色,需染色,深搜后还需回溯啦
#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;
}