洛谷 P6474 [NOI Online #2 入门组] 荆轲刺秦王 题解--zhengjun
我一开始就打出来了,可是忘记一个很重要的剪枝,就是如果当前的步数已经超过答案的步数就不用搜了,还有就是每一个点的每一种状态都只能走到一次(其实就是走到一个点,之前使用了相同的魔法已经到过这个点)那么也不用搜下去了。
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,c1,c2,d;//如题
int sx,sy,tx,ty;//起点终点坐标
string s;//读入的数据
int a[351][351];
int flag[351][351];
bool v[351][351][16][16];//剪枝二
void add(int i,int j,int x){
for(int k=-x+1;k<=x-1;k++){//差分使复杂度降为n^3
if(k+i<1||k+i>n)continue;
int p=x-1-(k<0?-k:k);//可以自己找规律
if(j-p<1)a[k+i][0]+=1;
else a[k+i][j-p]+=1;
if(j+p+1>m);
else a[k+i][j+p+1]-=1;
}
}
struct zj{
int x,y,u1,u2,t;//坐标,隐身使用次数,瞬移使用次数,已经过了多长时间
};
int ans1=0x3fffffff,ans2=0x3fffffff,ans=0x3fffffff;
int X[8]={0,0,1,-1,1,1,-1,-1};
int Y[8]={1,-1,0,0,1,-1,1,-1};
void bfs(){
queue<zj> q;
q.push((zj){sx,sy,0,0,0});
v[sx][sy][0][0]=1;//起点已经过
while(!q.empty()){
zj x=q.front();
q.pop();
if(x.t>ans)continue;//剪枝一
if(x.x==tx&&x.y==ty){
if(x.t<ans){
ans=x.t;
ans1=x.u1;
ans2=x.u2;
}
else{
if(ans1+ans2>x.u1+x.u2){//魔法使用次数少
ans=x.t;
ans1=x.u1;
ans2=x.u2;
}
else if(ans1+ans2==x.u1+x.u2&&ans1>x.u1){//魔法一样,隐身少
ans=x.t;
ans1=x.u1;
ans2=x.u2;
}
}
continue;
}
for(int i=0;i<8;i++){
int xx=x.x+X[i],yy=x.y+Y[i];
if(xx<1||xx>n||yy<1||yy>m)continue;//越界
if(flag[xx][yy]==1)continue;//有士兵
if(a[xx][yy]<=0&&v[xx][yy][x.u1][x.u2]==0){//不在士兵的观察范围内
v[xx][yy][x.u1][x.u2]=1;//标记
q.push((zj){xx,yy,x.u1,x.u2,x.t+1});
}
else if(x.u1+1<=c1&&v[xx][yy][x.u1+1][x.u2]==0){//在士兵的观察范围内,使用隐身
v[xx][yy][x.u1+1][x.u2]=1;//标记
q.push((zj){xx,yy,x.u1+1,x.u2,x.t+1});
}
}
if(x.u2+1>c2)continue;//无法使用瞬移
for(int i=0;i<4;i++){
int xx=x.x+X[i]*d,yy=x.y+Y[i]*d;
if(xx<1||xx>n||yy<1||yy>m)continue;
if(flag[xx][yy]==1)continue;
if(a[xx][yy]<=0&&v[xx][yy][x.u1][x.u2+1]==0){
v[xx][yy][x.u1][x.u2+1]=1;
q.push((zj){xx,yy,x.u1,x.u2+1,x.t+1});
}
else if(x.u1<c1&&v[xx][yy][x.u1+1][x.u2+1]==0){
v[xx][yy][x.u1+1][x.u2+1]=1;
q.push((zj){xx,yy,x.u1+1,x.u2+1,x.t+1});
}
}
}
}
int main(){
scanf("%d%d%d%d%d",&n,&m,&c1,&c2,&d);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>s;
if(s=="S")flag[i][j]=-2,sx=i,sy=j;
else if(s=="T")flag[i][j]=-1,tx=i,ty=j;
else if(s==".");
else{
flag[i][j]=1;
int x=s[0]-'0';
for(int i=1;i<s.length();i++)x=x*10+s[i]-'0';//像个快读
add(i,j,x);//差分
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]+=a[i][j-1];//注意差分要加回去
}
}
bfs();
if(ans==0x3fffffff)printf("-1");//无解
else printf("%d %d %d",ans,ans1,ans2);//输出
return 0;
}