问题来源
BYVoid魔兽世界模拟赛
【问题描述】
一万两千年前,当精灵还是在艾萨拉女王的统治下的时候,辛德拉就是是女王手下一名很有地位的法师了。他受任建造了一座城市,来保存女王的法师们进行魔法研究的成果和法术物品。这个城市就是埃雷萨拉斯。永恒之井爆炸以后,埃雷萨拉斯的精灵和艾萨拉联系中断,并失去了永恒之井的水做为能量的来源。辛德拉的后人为了对满足魔法的欲望,他们捕猎了一个恶魔,伊莫塔尔。他们用水晶塔建造了一个带有能量平衡系统的结界*,水晶塔从恶魔身上吸取能量,一部分维持结界*,一部分可以让*的精灵们吸收。这个系统万年以来一直平安无事,可是现在,随着恶魔的能量被消耗殆尽,已经难以维持结界*的消耗。统治者托尔塞林王子为了满足自己的欲望,开始下令*,除了少数*者之外的其他人都要死,这样才能减少对魔法能量的消耗。终于有一天,戈多克食人魔成功入侵了埃雷萨拉斯,并杀死了几乎所有的精灵。他们把这里当作自己王国的领地,名叫厄运之槌。面临着灭顶之灾的精灵们把他们祖先留下的宝藏用魔法结界藏了起来,以防戈多克食人魔抢走。
作为一名勇敢的探险者,你悄悄来到了埃雷萨拉斯寻找传说中的宝藏。终于,你看见宝藏就在你的前方不远处。但是你不能贸然前进,因为路上有着强大的魔法结界。这些结界根据能量的不同分为P种,踏入每种结界,你都会受到一定的伤害。为了拿到宝藏,这些伤害算不了什么。但是你要尽可能地减少伤害,请你设计一条路线,使你穿越结界获取宝藏受到的伤害最少。下面是一个魔法结界能量示意图,结界是一个正方形,内部有P种不同的能量,每种字母表示一种能量。你从最上端开始走,每次可以走到与你所在的位置上下左右相邻的临位,或者在同种能量结界中任意传送。重复进入同一种能量结界不会再次受到伤害。
|AAABBC|
|ABCCCC|
|AABBDD|
|EEEEEF|
|EGGEFF|
|GGFFFF|
你有H点生命值,请你在贸然行动之前先判断是否能够活着(生命值大于0)穿越结界拿到宝藏,如果能够,请求出最多剩余的生命值。
【输入格式】
第1行 三个非负整数 N,P,H。N为结界的边长,P为不同的能量结界的数量,H为你的生命值。
第2-P+1行 每行一个非负整数,表示走到该种能量结界受到的伤害值。
第P+2至第P+2+N行 每行N个正整数,为地图上该单元格的能量种类的编号,编号为1..P。
【输出格式】
如果你能够穿越结界到达对岸的宝藏,输出最多剩余的生命值。如果不能穿越,输出NO。
【输入样例】
6 7 10
3
1
2
2
1
1
3
1 1 1 2 2 3
1 2 3 3 3 3
1 1 2 2 4 4
5 5 5 5 5 6
5 7 7 5 6 6
7 7 6 6 6 6
【输出样例】
7
【数据说明】
路线为
起始-2-5-6-目标
1 1 1 2 2 3
1 2 3 3 3 3
1 1 2 2 4 4
5 5 5 5 5 6
5 7 7 5 6 6
7 7 6 6 6 6
对于40%数据
4<=N<=10 对于100%数据
4<=N<=50
1<=P<=N*N
0<=H<=200000000
分析
最短路的变形,添加超级源(到第一行的距离为0),加入标记的传递(记录经过那些结界)就可以AC了,考试时由于没处理好所以WA了一个点。
/*
ID: ringxu97
LANG: C++
TASK: eldrethalas
SOLUTION: 最短路
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
using namespace std; typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int maxn=+;
const int maxp=maxn*maxn;
int N,P,H;
int hurt[maxp];//进入受到的伤害
int G[maxn][maxn];//记录
bool vis[maxn][maxn][maxp];//是否到过该种结界
int dist[maxn][maxn];//dist[i][j]表示到i,j的最小代价
bool inq[maxn][maxn];//i,j节点是否在数组中
queue<pii>Q; void init()//初始化
{
memset(hurt,,sizeof(hurt));
memset(vis,,sizeof(vis));
memset(inq,,sizeof(inq));
for(int i=;i<=N;++i)
for(int j=;j<=N;++j)
{
dist[i][j]=inf;
}
}
void read()//读入数据
{ scanf("%d%d%d",&N,&P,&H);
init();
for(int i=;i<=P;++i)scanf("%d",hurt+i);
for(int i=;i<=N;++i)
for(int j=;j<=N;++j)
{
scanf("%d",&G[i][j]);
if(i==)//初始化第一行
{
dist[i][j]=hurt[G[i][j]];
Q.push(make_pair(i,j));
vis[i][j][G[i][j]]=;
inq[i][j]=;
}
}
}
const int dx[]={,,,,-};
const int dy[]={,,-,,};
inline bool check(int i,int j){return (<=i && i<=N && <=j && j<=N);}
void SPFA()
{
while(!Q.empty())
{
pii u=Q.front();Q.pop();
inq[u.first][u.second]=;
//vis[u.first][u.second][G[u.first][u.second]]=1;
for(int k=;k<=;++k)if(check(u.first+dx[k],u.second+dy[k]))//扩展周围节点
{
//pii v=make_pair(u.first+dx[k],u.second+dy[k]);
int relax=dist[u.first][u.second];
if(G[u.first+dx[k]][u.second+dy[k]]!=G[u.first][u.second]/*结界种类不同*/ && !vis[u.first][u.second][G[u.first+dx[k]][u.second+dy[k]]]) /*没有到过该种结界*/
{
relax+=hurt[G[u.first+dx[k]][u.second+dy[k]]];//加入伤害
}
if(dist[u.first+dx[k]][u.second+dy[k]]>relax)
{
dist[u.first+dx[k]][u.second+dy[k]]=relax;//松弛
if(!inq[u.first+dx[k]][u.second+dy[k]])
{
memcpy(vis[u.first+dx[k]][u.second+dy[k]],vis[u.first][u.second],sizeof(vis[u.first+dx[k]][u.second+dy[k]]));
vis[u.first+dx[k]][u.second+dy[k]][G[u.first+dx[k]][u.second+dy[k]]]=;
inq[u.first+dx[k]][u.second+dy[k]]=;
Q.push(make_pair(u.first+dx[k],u.second+dy[k]));
}
}
}
for(int i=;i<=N;++i)//扩展图中同类结界
for(int j=;j<=N;++j)
if(G[i][j]==G[u.first][u.second] && dist[i][j]>dist[u.first][u.second])//不在队列中 AND 同类 AND 可以松弛
{
vis[i][j][G[i][j]]=;
dist[i][j]=dist[u.first][u.second];//松弛
if(!inq[i][j])
{
inq[i][j]=;//加入队列
Q.push(make_pair(i,j));
memcpy(vis[i][j],vis[u.first][u.second],sizeof(vis[i][j]));
} }
}
} void print()//打印
{
/*
for(int i=1;i<=N;++i)
for(int j=1;j<=N;++j)
{
printf("%2d ",dist[i][j]);
if(j==N)printf("\n");
}*/
int ans=inf;
for(int i=;i<=N;++i)
{
ans=min(ans,dist[N][i]);
}
ans=H-ans;
if(ans>)printf("%d\n",ans);
else printf("NO\n");
}
int main()
{
freopen("eldrethalas.in", "r", stdin);
freopen("eldrethalas.out", "w", stdout);
read();
SPFA();
print();
return ;
}