UVALive - 4618 Wormholes(负环)

来源:互联网 发布:网络诈骗安全选择题 编辑:程序博客网 时间:2024/06/05 14:52

题目大意:给出出发点和终点和m个虫洞(虫洞的出发点,终点,生成时间和花费时间),问从起点到终点花费的最小时间

解题思路:关键是有负环,所以直接跑最短路算法的话会TLE,所以负环要处理一下
但是这个负环又不是负环,因为负环到一定程度的话,就会消失。
比如,到达时间小于虫洞的生成时间,那么负环就消失了,也就是说,负环内的点满足的最优情况就是到达时间刚好等于生成时间
所以,先找出负环,接着判断一下这个负环内的能节省的最多时间,然后将所有点都减去那个时间,那么负环就消失了
具体看代码

#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;#define N 110#define INF 0x3f3f3f3fstruct Point{    int x, y, z;}s1, s2;struct wormhole{    Point s, t;    int start, cost;}W[N];int n, source, sink;int dis[N][N];int distance(int i, int j) {    int x = W[i].t.x - W[j].s.x;    int y = W[i].t.y - W[j].s.y;    int z = W[i].t.z - W[j].s.z;    return ceil(sqrt(1.0 * x * x + 1.0 * y * y + 1.0 * z * z));}void init() {    scanf("%d%d%d%d%d%d%d", &s1.x, &s1.y, &s1.z, &s2.x, &s2.y, &s2.z, &n);    source = 0;    sink = n + 1;    W[0].s = s1;    W[0].t = s1;    W[0].start = -INF;    W[0].cost = 0;    W[sink].s = s2;    W[sink].t = s2;    W[sink].start =-INF;    W[sink].cost = 0;    for (int i = 1; i <= n; i++)        scanf("%d%d%d%d%d%d%d%d", &W[i].s.x, &W[i].s.y, &W[i].s.z, &W[i].t.x, &W[i].t.y, &W[i].t.z, &W[i].start, &W[i].cost);    //dis[i][j]表示从第i个虫洞的终点走到第j个虫洞的出发点的时间    for (int i = 0; i <= sink; i++)        for (int j = 0; j <= sink; j++)             dis[i][j] = distance(i, j);}int pre[N], d[N];void solve() {    for (int i = 0; i <= sink; i++) {        d[i] = INF;        pre[i] = -1;    }    d[source] = 0;    while (1) {        int tmp;        bool flag = false;        //找最短路        for (int i = 0; i <= sink; i++)            for (int j = 0; j <= sink; j++) {                tmp = max(d[i] + dis[i][j], W[j].start) + W[j].cost;                if (tmp < d[j]) {                    d[j] = tmp;                    pre[j] = i;                    flag = true;                }            }        //不用更新了        if (!flag) {            printf("%d\n", d[sink]);            return ;        }        for (int i = 0; i <= sink; i++) {            int k = i;            //判断有没有环            for (int j = 0; j <= sink && k != -1; j++)  k = pre[k];            if (k == -1) continue;            //找出更新的值,破除这个负环,更新的值就是到达起点的值和生成时间的差的最小值,因为在负环内可以等待到生成时间            int Min = d[pre[k]] + dis[pre[k]][k] - W[k].start;            for (int j = pre[k]; j != k; j = pre[j])                Min = min(d[pre[j]] + dis[pre[j]][j] - W[j].start, Min);            //如果不存在负环了            if (Min <= 0) continue;            //更新负环内的点的距离,破解负环                d[k] -= Min;            for (int j = pre[k]; j != k; j = pre[j])                d[j] -= Min;        }    }}int main() {    int test;    scanf("%d", &test);    while (test--) {        init();        solve();    }    return 0;}
1 1
原创粉丝点击