题目戳这里 一道又标准又有点变形的广搜题(?)
思路
本题难点在于对各种状态的确认 我们把状态分为两大类( 我觉得这样比较清晰 ): 注意:这里认为下一个块(也就是欲扩展块)未被走过
- 下一个块没有颜色
对于当前状态,有以下状态以及操作: (1) 当前踩的块是被施法过的,不能施法,继续寻找其他扩展块 (2) 当前踩的块没有被施法过,对下一个块染成和当前块一样的颜色,代价+2 - 下一个块有颜色
对于当前状态,有以下状态以及操作: (1) 当前块与下一个块颜色相同,扩展且无需任何代价 (2) 当前块与下一个块颜色相同,扩展且无需任何代价
现在讲讲为什么要把没有颜色的可扩展点变成和当前点相同颜色的点(以下用假设法来说明正确性,有点小乱) 假设当前块为红色,第二个块(要踩的块)为无色。我们首先将第二个块染成黄色,代价是2,我们踩上去。假如第三个块为可扩展块且是红色,我们又要花1的代价走过去,总共是3的代价(这不是很明显的白给嘛 )如果我们把第二个块染成红色,总共只花了2个代价就能过去了。假如第三个块是黄色,同样的,无论第二个块是黄色还是红色,我们都只需要花3个代价就能过去了。因此,我们对于无色块只需要染成与第一个块同样的颜色就可以了。
这就讲完了。
只需要按照上面对状态的分类就可以写出来啦
对了,这题用普通队列是过不了的,必须要用优先队列(具体为什么我也不知道)
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define ll long long
#define re register
using namespace std;
const int N=5e5+10;
const int mod=1e9+7;
struct node{
int x,y,sum;
bool flag;
bool operator<(const node &aa) const
{
return sum>aa.sum;
}
};
int cx[4]={0,0,1,-1};
int cy[4]={1,-1,0,0};
int a[2001][2001];
bool vis[2001][2001];
priority_queue<node> q;
int main()
{
#ifndef ONLINE_JUDGE
freopen("q.in","r",stdin);
freopen("q.out","w",stdout);
#endif
int n,m;
cin>>n>>m;
for(int i=1;i<=m;++i)
{
int x,y,c;
cin>>x>>y>>c;
a[x][y]=c+1;
}
node t;
t.x=1;t.y=1;t.sum=0;t.flag=0;
q.push(t);
vis[1][1]=1;
while(!q.empty())
{
t=q.top();
q.pop();
if(t.x==n&&t.y==n)
{
cout<<t.sum<<endl;
return 0;
}
for(int i=0;i<4;++i)
{
int tx=t.x+cx[i];
int ty=t.y+cy[i];
node tt;
if(tx>0&&tx<=n&&ty>0&&ty<=n&&vis[tx][ty]==0)
{
if(a[tx][ty]!=0)
{
if(a[tx][ty]==a[t.x][t.y])
{
tt.x=tx;tt.y=ty;tt.sum=t.sum;tt.flag=0;
vis[tx][ty]=1;
q.push(tt);
}
else if(a[tx][ty]!=a[t.x][t.y])
{
tt.x=tx;tt.y=ty;tt.sum=t.sum+1;tt.flag=0;
vis[tx][ty]=1;
q.push(tt);
}
}
else
{
if(t.flag==0)
{
a[tx][ty]=a[t.x][t.y];
tt.x=tx;tt.y=ty;tt.sum=t.sum+2;tt.flag=1;
vis[tx][ty]=1;
q.push(tt);
}
}
}
}
if(t.flag==1) a[t.x][t.y]=0,t.flag=0;
}
cout<<-1<<endl;
return 0;
}
|