洛谷 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;
}

谢谢--zhengjun

posted @ 2022-06-11 13:48  A_zjzj  阅读(48)  评论(0编辑  收藏  举报