[题解] BYVoid 魔兽世界模拟赛 Stage.1 埃雷萨拉斯寻宝 (缩点最短路)

- 传送门 -

 https://www.byvoid.com/zhs/blog/byvoid-wow-stage-1

#埃雷萨拉斯寻宝
##问题描述 一万两千年前,当精灵还是在艾萨拉女王的统治下的时候,辛德拉就是是女王手下一名很有 地位的法师了。他受任建造了一座城市,来保存女王的法师们进行魔法研究的成果和法术物 品。这个城市就是埃雷萨拉斯。永恒之井爆炸以后,埃雷萨拉斯的精灵和艾萨拉联系中断, 并失去了永恒之井的水做为能量的来源。辛德拉的后人为了对满足魔法的欲望,他们捕猎了 一个恶魔,伊莫塔尔。他们用水晶塔建造了一个带有能量平衡系统的结界监狱,水晶塔从恶 魔身上吸取能量,一部分维持结界监狱,一部分可以让狂热的精灵们吸收。这个系统万年以 来一直平安无事,可是现在,随着恶魔的能量被消耗殆尽,已经难以维持结界监狱的消耗。 统治者托尔塞林王子为了满足自己的欲望,开始下令屠杀,除了少数狂热者之外的其他人都 要死,这样才能减少对魔法能量的消耗。终于有一天,戈多克食人魔成功入侵了埃雷萨拉斯, 并杀死了几乎所有的精灵。他们把这里当作自己王国的领地,名叫厄运之槌。面临着灭顶之 灾的精灵们把他们祖先留下的宝藏用魔法结界藏了起来,以防戈多克食人魔抢走。 作为一名勇敢的探险者,你悄悄来到了埃雷萨拉斯寻找传说中的宝藏。终于,你看见宝藏就 在你的前方不远处。但是你不能贸然前进,因为路上有着强大的魔法结界。这些结界根据能 量的不同分为 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。 BYVoid http://www.byvoid.com 9 / 10 ##输出格式 如果你能够穿越结界到达对岸的宝藏,输出最多剩余的生命值。如果不能穿越,输出 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 BYVoid http://www.byvoid.com 10 / 10 ##数据规模 对于 40%数据 4<=N<=10 对于 100%数据 4<=N<=50 1<=P<=N*N 0<=H<=200000000   ### - 题意 -  在上面的示意图中, 同种点有各自的权值, 可以在内部传送.  求一条从上到下的权值最小路.  若权值最小仍大于H值.  输出"NO".   ### - 思路 -  考完一个拆点分层最短路后的练习题...  这题考缩点, 拆点的题可以参考[营救大兵瑞恩][1], [汽车加油行驶问题][2].    因为一对点连在一起很繁琐.  所以考虑把同种点看成一个, 与之相连的点全按类别连上.  理解这个思想就行了, 注意细节...~~(我写的好吃藕哦!!!)~~    细节见代码.   ### - 代码 - ```c++ #include #include #include using namespace std;

const int N = 55;
const int NN = 2505;
const int M = 20000;

int MAP[N][N], C[N][N], DIS[NN];
int P[NN], VIS[NN];
int HD[NN], TO[M], NXT[M];
int X[4] = {0,0,1,-1};
int Y[4] = {1,-1,0,0};
int n, p, h, s, t, sz;
queue q;

void add(int x, int y) {
TO[sz] = y; NXT[sz] = HD[x]; HD[x] = sz++;
TO[sz] = x; NXT[sz] = HD[y]; HD[y] = sz++;
}

void dfs(int x, int y, int d) {
if (MAP[x][y] != d) {
if (!VIS[MAP[x][y]]) {
VIS[MAP[x][y]] = 1;
add(d, MAP[x][y]);
} //缩点
return;
}
C[x][y] = 1;
for (int i = 0; i < 4; ++i) {
int xx = x + X[i], yy = y + Y[i];
if (xx > 0 && xx <= n && yy > 0 && yy <= n)
if (C[xx][yy] == 0)
dfs(xx, yy, d);
}
}

void spfa() {
memset(DIS, 0x3f, sizeof (DIS));
DIS[s] = 0;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
VIS[u] = 0;
for (int i = HD[u]; i != -1; i = NXT[i]) {
int v = TO[i];
if (DIS[v] > DIS[u] + P[v]) {
DIS[v] = DIS[u] + P[v];
if (!VIS[v]) {
VIS[v] = 1;
q.push(v);
}
}
}
}
if (DIS[t] >= h) printf("NO\n");
else printf("%d\n", h - DIS[t]);
}

int main() {
freopen("eldrethalas.in","r",stdin);
freopen("eldrethalas.out","w",stdout);
memset(HD, -1, sizeof (HD));
scanf("%d%d%d", &n, &p, &h);
for (int i = 1; i <= p; ++i)
scanf("%d", &P[i]);
t = p + 1; //缩完点后点的编号为 1 到 p
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
scanf("%d", &MAP[i][j]);
}
for (int i = 1; i <= n; ++i) {
if (!VIS[MAP[1][i]]) {
VIS[MAP[1][i]] = 1;
add(s, MAP[1][i]);
}
}
memset(VIS, 0, sizeof (VIS));
for (int i = 1; i <= n; ++i) {
if (!VIS[MAP[n][i]]) {
VIS[MAP[n][i]] = 1;
add(MAP[n][i], t);
}
} //连起点终点
memset(VIS, 0, sizeof (VIS));
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (!C[i][j]) {
dfs(i, j, MAP[i][j]);
memset(VIS, 0, sizeof (VIS));
}
spfa();
return 0;
}



  [1]: http://www.cnblogs.com/Anding-16/p/7435266.html
  [2]: http://www.cnblogs.com/Anding-16/p/7434599.html
posted @ 2017-08-28 21:10  lstttt  阅读(288)  评论(0)    收藏  举报