题目大意:
题目链接:http://10.156.31.134/contestnew.aspx?cid=123 (学校局域网)
从第一行任一格子出发,到最后一行任一格子,可以传送到相同的魔法格里,经过不同的魔法格会损伤不同的生命(如果之前经过过就不会)。求最大剩余生命。
思路:
显然最短路啊。
因为同一个魔法格内可以互相传送,所以就按照魔法格的点权建点。
把每一个点拆成入点和出点,之间连长度为这个点损耗的生命值的边。
接下来,如果点和有相邻,那么在这两个点分别相连。
然后跑一边就可以了。
代码:
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#define mp make_pair
using namespace std;
const int N=2510;
int n,m,hp,ans,tot,head[N+N],dis[N+N],map[N][N];
bool used[N][N],vis[N+N];
struct edge
{
int dis,next,to;
}e[N*N*4+N];
int read()
{
int d=0;
char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
void add(int from,int to,int dis)
{
e[++tot].to=to;
e[tot].dis=dis;
e[tot].next=head[from];
head[from]=tot;
}
void dij()
{
memset(dis,0x3f3f3f3f,sizeof(dis));
priority_queue<pair<int,int> > q;
for (int i=1;i<=n;i++)
if (!vis[map[1][i]])
{
vis[map[1][i]]=1;
q.push(mp(0,map[1][i]));
dis[map[1][i]]=0;
}
memset(vis,0,sizeof(vis));
while (q.size())
{
int u=q.top().second,v;
q.pop();
if (vis[u]) continue;
vis[u]=1;
for (int i=head[u];~i;i=e[i].next)
{
v=e[i].to;
if (dis[v]>dis[u]+e[i].dis)
{
dis[v]=dis[u]+e[i].dis;
q.push(mp(-dis[v],v));
}
}
}
}
int main()
{
freopen("data.txt","r",stdin);
memset(head,-1,sizeof(head));
n=read(),m=read(),hp=read();
for (int i=1;i<=m;i++)
add(i,i+m,read());
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
map[i][j]=read();
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
if (i>1&&!used[map[i][j]][map[i-1][j]]) add(map[i][j]+m,map[i-1][j],0),used[map[i][j]][map[i-1][j]]=1;
if (i<n&&!used[map[i][j]][map[i+1][j]]) add(map[i][j]+m,map[i+1][j],0),used[map[i][j]][map[i+1][j]]=1;
if (j>1&&!used[map[i][j]][map[i][j-1]]) add(map[i][j]+m,map[i][j-1],0),used[map[i][j]][map[i][j-1]]=1;
if (j<m&&!used[map[i][j]][map[i][j+1]]) add(map[i][j]+m,map[i][j+1],0),used[map[i][j]][map[i][j+1]]=1;
}
dij();
ans=1e9;
for (int i=1;i<=n;i++)
if (dis[map[n][i]+m]<ans) ans=dis[map[n][i]+m];
if (ans<hp) printf("%d",hp-ans);
else printf("NO");
return 0;
}