题目链接:http://nyoj.top/problem/307

  • 内存限制:64MB 时间限制:1000ms

题目描述

传说HMH大沙漠中有一个迷宫,里面藏有许多宝物。迷宫里可能有N个藏宝地点,用1到N标记。藏宝地点之间最多有一条通路相连。标记1为迷宫的进出口。

某天,Dr.Kong找到了迷宫的地图,他已经知道其中K(1<=K<=N)个不同的地点真的藏有宝物。Dr.Kong决定让他的机器人卡多去探险。卡多在经过某个藏宝地点时可能会拿走宝物。但它每拿走一个藏宝地点的宝物后,它的载重量就会增加W。迷宫中的通路不是平坦的,到处都是陷阱。假设每条通路都有一个危险度,其值与通过此路的载重量成正比。

当机器人卡多进入迷宫时,它的载重量为0。只有当卡多携带宝物的载重量不大于某个通路的危险度时,它才能顺利通过此条道路,否则就会掉入陷阱,不能出来。
Dr.Kong希望他的机器人卡多尽量多的带出宝物,当然他更希望卡多最后能从标记1的地点走出去。

请你编写程序,帮助Dr.Kong计算一下,卡多最多能带出多少个藏宝地点的宝物。

输入描述

有多组测试数据,以EOF为输入结束的标志
第1行:N M K W
接下来有K行,每行一个整数,表示藏有宝物的地点标号。
再接下来有M行,每行三个整数X,Y,Z,表示地点X与地点Y之间有一条危险度为Z的通路。
1 ≤ N ≤ 8000,1 ≤ K ≤ N, 1 ≤ M ≤ 15000,1 ≤ W, Z ≤ 10000
数据保证所有的地点之间都是有道路可以到达的。
提示:机器人卡多经过一个藏宝地点时可以不拿走宝物, 而且同一个藏宝地点可以经过多次。

输出描述

输出有一个整数, 表示卡多最多能带出的宝物的堆数。

样例输入

6 7 5 1
1
2
3
4
5
1 2 3
3 6 2
6 2 10
2 4 1
5 1 1
4 5 1
1 6 1

样例输出

4

解题思路

最短路变型题,因为进出口都是在1号,并且同一个藏宝地点可以经过多次,那么我们可以先求出1号到其它每个地点的最大载重量,这样就可看成一条线把所有藏宝地点连在了一起。我们可以先拿那些载重量小的,然后再往大的拿,这样才能保证拿的最多。

#include <bits/stdc++.h>
using namespace std;
const int N = 8005;
const int inf = INT_MAX;
struct edge {
    int u, v, w;
}e[N << 2];
int dis[N], f[N], vis[N], maxv[N], loc[N], cnt;
void Add(int u, int v, int w) {
    e[++cnt] = (edge){f[u], v, w};
    f[u] = cnt;
}
void Spfa(int s) {
    queue <int> Q;
    Q.push(s);
    vis[s] = 1;
    dis[s] = inf;
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        vis[u] = 0;
        for (int i = f[u]; i; i = e[i].u) {
            int v = e[i].v;
            if (dis[v] < min(dis[u], e[i].w)) {
                dis[v] = min(dis[u], e[i].w);
                if (!vis[v]) {
                    Q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
} 
int main() {
    int n, m, k, w, a, b, c;
    while (~scanf("%d%d%d%d", &n, &m, &k, &w)) {
        cnt = 0;
        memset(f, 0, sizeof(f));
        memset(vis, 0, sizeof(vis));
        memset(dis, 0, sizeof(dis));
        for (int i = 0; i < k; i++)
            scanf("%d", &loc[i]);
        while (m--) {
            scanf("%d%d%d", &a, &b, &c);
            Add(a, b, c);
            Add(b, a, c);
        }
        Spfa(1);
        for (int i = 0; i < k; i++)
            maxv[i] = dis[loc[i]];
        sort(maxv, maxv + k);
        int total = 0;
        for (int i = 0; i < k; i++)//先到最小的藏宝点带上宝物, 再去次小 
            if (maxv[i] >= (total + 1) * w)
                total++;
        printf("%d\n", total);
    }
    return 0;
}