解题思路
调了我一个上午加一个中午的题啊啊啊啊啊!!!
对于每一个点记录{ x , y , c 1 x,y,c1 x,y,c1隐身使用次数, c 2 c2 c2瞬移使用次数,步数t}
对于一个点,向它能走到的点尝试扩展
-
没有用瞬移
新点没有士兵监视,不是士兵, x + w a x [ i ] , y + w a y [ i ] , c 1 , c 2 , t + 1 {x + wax[i], y + way[i], c1, c2, t+1} x+wax[i],y+way[i],c1,c2,t+1
如果新点是士兵监视,但不是士兵, x + w a x [ i ] , y + w a y [ i ] , c 1 + 1 , c 2 , t + 1 {x + wax[i], y + way[i], c1+1, c2, t+1} x+wax[i],y+way[i],c1+1,c2,t+1 -
用了瞬移
新点没有士兵监视,不是士兵, x + w a x [ i ] ∗ d , y + w a y [ i ] ∗ d , c 1 , c 2 + 1 , t + 1 {x + wax[i] * d, y + way[i] * d, c1, c2+1, t+1} x+wax[i]∗d,y+way[i]∗d,c1,c2+1,t+1
如果新点是士兵监视,但不是士兵, x + w a x [ i ] ∗ d , y + w a y [ i ] ∗ d , c 1 + 1 , c 2 + 1 , t + 1 {x + wax[i] * d, y + way[i] * d, c1+1, c2+1, t+1} x+wax[i]∗d,y+way[i]∗d,c1+1,c2+1,t+1 -
剪枝
走到一个点可能有很多种走法,所以一个点的判重,要判[x][y][c1][c2]有无走过
答案的优良性包括{t,c1,c2},so第一次走到终点不一定是最优的,但是如果当前队列层的步数已经超过当前最优答案了,就可以直接退出了
如果方向是偶数,可以尝试瞬移
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <cmath>
using namespace std;
const int dx[8]= {-1,-1,0,1,1,1,0,-1};
const int dy[8]= {0,1,1,1,0,-1,-1,-1};
struct c {
int c1,c2,t;
}ans;
struct cc {
int x,y,c1,c2,t;
}f[5000000];
int h,t,n,m,c1,c2,d,sx,sy,tx,ty,a[500][500],b[500][500],v[500][500][20][20];
string c;
bool check(int x,int y) {
if(x>0&&y>0&&x<=n&&y<=m)
return 1;
return 0;
}
void answer(int x,int y,int cc1,int cc2,int tt){
if(x==tx&&y==ty)
{
if(tt<ans.t||ans.t==-1)//比当前答案优,或第一次到达终点
ans.c1=cc1,ans.c2=cc2,ans.t=tt;
else if(tt==ans.t)
{
if((cc1+cc2)<(ans.c1+ans.c2))//技能数和更少
ans.c1=cc1,ans.c2=cc2;
else
if((cc1+cc2)==(ans.c1+ans.c2)&&cc1<ans.c1)//隐身更少
ans.c1=cc1,ans.c2=cc2;
}
}
}
void bfs() {
int xx,yy;
h=0,t=1;
f[1].x=sx,f[1].y=sy,v[sx][sy][0][0]=1,ans.t=-1;
while(h!=t) {
h=(h%4999999)+1;
if(f[h].t>ans.t&&ans.t!=-1)//剪枝:已经超过当前最优答案了
break;
answer(f[h].x,f[h].y,f[h].c1,f[h].c2,f[h].t);
for(int i=0; i<8; i++) {
xx=f[h].x+dx[i],yy=f[h].y+dy[i];
if(check(xx,yy)) {//不使用瞬移
if(a[xx][yy]==0)//不在守卫监视范围——不使用隐身
{
if(!v[xx][yy][f[h].c1][f[h].c2])
{
t=(t%4999999)+1;
f[t]=(cc){xx,yy,f[h].c1,f[h].c2,f[h].t+1};
v[xx][yy][f[t].c1][f[t].c2]=1;
}
}
else if(f[h].c1<c1&&!v[xx][yy][f[h].c1+1][f[h].c2]&&!b[xx][yy])
{
t=(t%4999999)+1;
f[t]=(cc){xx,yy,f[h].c1+1,f[h].c2,f[h].t+1};
v[xx][yy][f[t].c1][f[t].c2]=1;
}
}
if(!(i%2)&&f[h].c2<c2) {//使用瞬移,↓同上
xx=f[h].x+dx[i]*d,yy=f[h].y+dy[i]*d;
if(check(xx,yy)) {
if(a[xx][yy]==0)
{
if(!v[xx][yy][f[h].c1][f[h].c2+1])
{
t=(t%4999999)+1;
f[t]=(cc){xx,yy,f[h].c1,f[h].c2+1,f[h].t+1};
v[xx][yy][f[t].c1][f[t].c2]=1;
}
}
else if(f[h].c1<c1&&!v[xx][yy][f[h].c1+1][f[h].c2+1]&&!b[xx][yy])
{
t=(t%4999999)+1;
f[t]=(cc){xx,yy,f[h].c1+1,f[h].c2+1,f[h].t+1};
v[xx][yy][f[t].c1][f[t].c2]=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>>c;
if(c==".")
continue;
if(c=="S")
{
sx=i,sy=j;
continue;
}
else if(c=="T")
{
tx=i,ty=j;
continue;
}
int w=c[0]-48;
for(int k=1; k<c.size(); k++)
w=w*10+c[k]-48;
for(int ii=max(1,i-w+1); ii<=min(i+w-1,n); ii++)
for(int jj=max(1,j-w+1); jj<=min(j+w-1,m); jj++)
if ((abs(ii-i)+abs(jj-j))<w)
a[ii][jj]++;//标记守卫监视范围
b[i][j]=1;//标记守卫位置
}
}
bfs();
if(ans.t!=-1)
printf("%d %d %d",ans.t,ans.c1,ans.c2);
else
printf("-1");
}