poj 3268 Silver Cow Party 图论 最短路

题目

题目链接:http://poj.org/problem?id=3268

题目来源:《挑战》练习题

简要题意:给定有向图,求所有点到 X 来回最短路之和的最小值。

数据范围:1N1000;1M105;1XN;1Ti100

题解

首先我自己开始做的时候是全程Dijkstra的,做法就是先做单源最短路,然后反向做单源最短路剪枝。

这个还比较好写,最后大概300+ms,然后看到有比我快很多的,翻了下题解。

实际上只需要建反图然后跑次最短路就能求出所有点到 X 的最短路了

利用反证法可以很容易证明这个结论是正确的,这个结论还是挺有用的。

于是之后又写了反图+spfa的版本。

慢的代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>

#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
const int N = 1005;
const int M = 1E5+5;
const int INF = 0x3f3f3f3f;

struct Edge {
    int to, nxt, c;
    Edge(int to, int nxt, int c) : to(to), nxt(nxt), c(c) {}
    Edge() {}
};

int head[N];
Edge e[M];
int dis[N];
int disb[N];

inline void addEdge(int from, int to, int c, int cnt) {
    e[cnt] = Edge(to, head[from], c);
    head[from] = cnt;
}

void init(int n) {
    for (int i = 0; i < n; i++) head[i] = -1;
}


int Dij(int st, int en, int n, int dis[]) {
    for (int i = 0; i < n; i++) dis[i] = INF;
    dis[st] = 0;
    priority_queue<PII, vector<PII>, greater<PII> > q;
    q.push(mp(0, st));

    while (!q.empty()) {
        PII cur = q.top();
        q.pop();
        if (dis[cur.se] < cur.fi) continue;
        if (cur.se == en) return dis[en];
        for (int i = head[cur.se]; ~i; i = e[i].nxt) {
            Edge &ee = e[i];
            if (dis[ee.to] > dis[cur.se] + ee.c) {
                dis[ee.to] = dis[cur.se] + ee.c;
                q.push(mp(dis[ee.to], ee.to));
            }
        }
    }
    return 0;
}
int main()
{
    int n, m, st, u, v, c, cnt = 0;
    scanf("%d%d%d", &n, &m, &st);
    st--;
    init(n);
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d", &u, &v, &c);
        addEdge(u-1, v-1, c, cnt++);
    }

    Dij(st, -1, n, dis);
    int ans = 0;
    for (int i = 0; i < n; i++) {
        ans = max(ans, dis[i]+Dij(i, st, n, disb));
    }
    printf("%d\n", ans);
    return 0;
}

反图代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>

#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
const int N = 1e3+5;
const int M = 2E4+5;
const int INF = 0x3f3f3f3f;

struct Edge {
    int to, nxt, c;
    Edge(int to, int nxt, int c) : to(to), nxt(nxt), c(c) {}
    Edge() {}
};

int head[N];
int headr[N];
Edge e[M];
Edge er[M];

void addEdge(int from, int to, int c, int cnt, int head[], Edge e[]) {
    e[cnt] = Edge(to, head[from], c);
    head[from] = cnt;
}

void init(int n) {
    for (int i = 0; i < n; i++) head[i] = headr[i] = -1;
}

bool vis[N];
int dis[N];
int disr[N];

void spfa(int st, int n, int dis[], int head[], Edge e[]) {
    for (int i = 0; i < n; i++) {
        vis[i] = 0;
        dis[i] = INF;
    }

    queue<int> q;
    q.push(st);
    vis[st] = true;
    dis[st] = 0;
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        vis[cur] = false;
        for (int i = head[cur]; ~i; i = e[i].nxt) {
            Edge &ee = e[i];
            if (ee.c + dis[cur] < dis[ee.to]) {
                dis[ee.to] = ee.c + dis[cur];
                if (!vis[ee.to]) vis[ee.to] = true, q.push(ee.to);
            }
        }
    }
}

int main()
{
    int n, m, st, u, v, c, cnt = 0;
    scanf("%d%d%d", &n, &m, &st);
    st--;
    init(n);
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d", &u, &v, &c);
        u--, v--;
        addEdge(u, v, c, cnt, head, e);
        addEdge(v, u, c, cnt++, headr, er);
    }

    spfa(st, n, dis, head, e);
    spfa(st, n, disr, headr, er);
    int ans = 0;
    for (int i = 0; i < n; i++) {
        ans = max(ans, dis[i] + disr[i]);
    }
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值