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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章