【题目链接】http://acm.hdu.edu.cn/showproblem.php?pid=3442
【解题报告】
题目状态数很多,所以按照题意先敲了一个图出来,对每个点的cost值进行了处理,后面如何取得最短路就不会了。还是太弱,看了题解才有思路。
BFS+优先队列。
显然BFS是路径查找的必要手段,那么优先队列有什么用呢?如果我们从一个点开始寻找路径,那么我们会把与它相连的四条路径(如果都走得通的话)都搜索一遍,如果我们要确保得到最短路,那么我们每次应当保证当前取出的一个状态是所有压入队列里的状态中cost值最小的(贪心的想)。
到了这里还有一个问题,如果取过一种类型的cost值(watchtower, forts, Archers, ordinary soldiers),如何保证我们在接下来的路径查找中不再次加上该种类型的cost值?显然,如果我们第一次加入这种类型,那么我们对这种类型加一个标记即可。
于是为了求解的清晰,我们应当使用结构体来保存每个路径状态,变量值应当包含:当前状态的末节点;标记数组(使用状态压缩最为方便);当前路径的总花费。BFS搜到终点即可返回。具体细节请参阅代码。
ps:这道题9月敲还不过,12月敲的时候,虽然敲的时间还是很长,不过各个细节都想的很清楚了。所以就1A了。虽然成长很慢,但是还是很高兴。
【参考代码】
/*
没想好导致写挫了,除了维护mp数组,还必须维护hurt数组,因为不同类型的伤害是分开算的
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int INF=1e9;
const int fx[5]={0, 0, 0, 1, -1 };
const int fy[5]={0, 1, -1,0, 0 };
struct state
{
int x,y,vis,val;
bool operator < ( const state& a )const
{
return val>a.val;
}
void sets( int x, int y, int vis, int val )
{
this->x=x;
this->y=y;
this->vis=vis;
this->val=val;
}
};
int N,M;
char str[55][55];
int mp[55][55];//表示这个点是否可达
int hurt[55][55][7];//记录每个点的伤害类型
int vis[55][55]; //表示一个点有没有被访问过
int startx,starty,finishx,finishy;
void set_attack( int x, int y, int range, int damage, int op ) //op表示伤害类型
{
for( int i=-range; i<=range; i++ )
for( int j=-range; j<=range; j++ )
{
if( abs(i)+abs(j)<=range && 1<=x+i && x+i<=N && 1<=y+j && y+j<=M )
{
hurt[i+x][j+y][op]+=damage;
}
}
}
void init()
{
for( int i=1; i<=N; i++ )
for( int j=1; j<=M; j++ )
{
if( str[i][j]=='$' ){ startx=i; starty=j; mp[i][j]=0; }
else if( str[i][j]=='!' ) { finishx=i; finishy=j; mp[i][j]=0; }
else if( str[i][j]=='#' ) { mp[i][j]=-1; }
else if( str[i][j]=='.' ) { continue; }
else if( str[i][j]=='A' ) { mp[i][j]=-1; set_attack( i,j,2,1,1 ); }
else if( str[i][j]=='B' ) { mp[i][j]=-1; set_attack( i,j,3,2,2 ); }
else if( str[i][j]=='C' ) { mp[i][j]=0; set_attack( i,j,0,3,3 ); }
else if( str[i][j]=='D' ) { mp[i][j]=-1; set_attack( i,j,2,4,4 ); }
else if( str[i][j]=='E' ) { mp[i][j]=-1; set_attack( i,j,1,5,5 ); }
}
}
int BFS()
{
// int kas=0; //print
priority_queue<state>q;
state q1;
q1.sets( startx,starty,0,0 );
vis[startx][starty]=1;
q.push(q1);
while( !q.empty() )
{
state qnow=q.top(); q.pop();
// cout<<++kas<<" "<<qnow.x<<" "<<qnow.y<<endl; //print
if( qnow.x==finishx && qnow.y==finishy ){ return qnow.val; }
for( int i=1; i<=4; i++ )
{
int tx=qnow.x+fx[i], ty=qnow.y+fy[i];
if( tx>N || tx<1 || ty>M || ty<1 )continue;
if( mp[tx][ty]==-1 )continue;
if( vis[tx][ty]==1 )continue;
int tval=qnow.val,tvis=qnow.vis;
for( int j=1; j<=5; j++ )
{
if( hurt[tx][ty][j] )
{
if( tvis&(1<<j) )continue;
tvis|=1<<j; tval+=j; //同一个点,同一种伤害不会叠加。要理解题意。
}
}
state qnext;
qnext.sets( tx,ty,tvis,tval );
q.push(qnext);
vis[tx][ty]=1;
}
}
return -1;
}
int main()
{
// freopen( "G.in","r",stdin );
int T,kase=0; cin>>T;
while( T-- )
{
memset( mp,0,sizeof mp );
memset( hurt,0,sizeof hurt );
memset( vis,0,sizeof vis );
scanf( "%d%d",&N,&M );
for( int i=1; i<=N; i++ ) scanf( "%s",&str[i][1] );
init();
int ans=BFS();
printf( "Case %d: %d\n",++kase,ans );
/* for( int i=1; i<=N; i++ )
for( int j=1; j<=M; j++ )
if( j==M )cout<<hurt[i][j][3]<<endl;
else cout<<hurt[i][j][3]<<" "; */ //print
}
return 0;
}