HDU 3416 Marriage Match IV (SPFA+最大流)

来源:互联网 发布:java面试问题 编辑:程序博客网 时间:2024/05/16 12:13

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3416

题意:

每条边最多只走一次,统计最短路径条数。

解题思路:

    看到题目,限制了走的次数,想到了网络流,容量为1,即为限制一次,又统计最短路径条数,想到最小费用最大流,在增流时判断现在增流的花费是否大于最短路径,也就是第一次增流的花费,如果大于,结束,如果不大于,继续。结果TLE
    看题解后,很强大。
    既然只统计最短路径条数,那么只有在最短路径的边是有用的,如果能统计出起始点到所有点的最短距离dist1,所有点到终止点的最短距离dist2,任意一条边,看这条边的起始点dist1加上边权加上终止点的dist2,如果等于最短路径,则在最短路径中,是有效的,如果不满足,则不再最短路径中,也就没有用了。
    找到由最短路径构成的图,跑一遍最大流,就得到答案。
    完成上述这的过程就需要dist1和dist2,也就是正图以起始点为起始点跑最短路,重新建逆图,逆图以终止点为起始点跑一遍最短路,就得到dist1和dist2.然后重新建图。这个就很强势了,让我知道了链式前向星怎么建逆图。之所以逆图表示所有点到终止点的距离,可以这样理解,逆图中终止点指向的边都是原本指向终止点的,那么,在逆图中跑的最短路,就是其他点跑向终止点的最短路。

#include <iostream>#include <cstdio>#include <cstring>#include <queue>using namespace std;typedef pair<int, int> P;const int N = 1010;const int M = 100005;const int INF = 1e9;struct edge {    int to, next;    int dist, c, f;}graph[M<<1];int totlen;int head[N];int n, m;int a[M], b[M], c[M];int s, t;// Dinicint level[N];int cur[N];bool dinic_bfs() {    memset(level, 0, sizeof(level));    queue<int> que;    que.push(s);    level[s] = 1;    while(!que.empty()) {        int u = que.front(); que.pop();        for(int i = head[u]; i != -1; i = graph[i].next) {            int v = graph[i].to;            if(!level[v] && graph[i].c > graph[i].f) {                level[v] = level[u]+1;                que.push(v);            }        }    }    return level[t] != 0;}int dinic_dfs(int u, int cpflow) {  // cpflow: can pass flow  到达u点最大能通过的流量    if(u == t) return cpflow;  // 到达汇点    int addflow = 0;  // u 点到其他点 最多能增广的流量, 最多不能超过cpflow,由前面的边限制    for(int& i = cur[u]; i != -1 && addflow < cpflow; i = graph[i].next){        int v = graph[i].to;        if(level[u]+1 == level[v] && graph[i].c > graph[i].f) {            // 这一条路上增广的流量            int tmpadd = dinic_dfs(v, min(cpflow-addflow, graph[i].c-graph[i].f));            if(tmpadd > 0) {                graph[i].f += tmpadd;  // 正向通过的流量加                graph[i^1].f -= tmpadd;  // 反向的流量就得减                return tmpadd;            }        }    }    return 0;  // 返回这个点都汇点能增广的流量}int dinic() {    int maxflow = 0;    int tmpflow = 0;    while(dinic_bfs()) {        memcpy(cur, head, sizeof(head));        while((tmpflow = dinic_dfs(s, INF)) > 0)                maxflow += tmpflow;    }    return maxflow;}// Dijkstrabool visit[N];int dist1[N], dist2[N];priority_queue<P, vector<P>, greater<P> > que;void dijkstra(int s, int* dist) {    for(int i = 0; i <= n; i++) dist[i] = INF;    for(int i = 0; i <= n; i++) visit[i] = 0;    while(!que.empty())  que.pop();    dist[s] = 0;    que.push(P(0, s));    while(!que.empty()) {        P cur = que.top();  que.pop();        int u = cur.second;        if(visit[u]) continue;        visit[u] = true;        for(int i = head[u]; i != -1; i = graph[i].next)        if(!visit[graph[i].to]) {            int v = graph[i].to;            if(dist[v] > dist[u]+graph[i].dist) {                dist[v] = dist[u]+graph[i].dist;                que.push(P(dist[v], v));            }        }    }}void addEdge(int u, int v, int c, int dist) {    if(u == v) return;    // 正向边    graph[totlen].to = v;    graph[totlen].next = head[u];    graph[totlen].dist = dist;    graph[totlen].c = c;    graph[totlen].f = 0;    head[u] = totlen++;    // 添加反向边    graph[totlen].to = u;    graph[totlen].next = head[v];    graph[totlen].dist = INF;    graph[totlen].c = 0;    graph[totlen].f = 0;    head[v] = totlen++;    // 正向边下标通过异或就得到反向边下标, 2 ^ 1 == 3 ; 3 ^ 1 == 2}void init() {    for(int i = 0; i <= n; i++)  head[i] = -1;    totlen = 0;}int main() {    int _;    scanf("%d", &_);    while(_--) {        scanf("%d%d", &n, &m);        for(int i = 0; i < m; i++){            int u, v, w;            scanf("%d%d%d", &a[i], &b[i], &c[i]);        }        scanf("%d%d", &s, &t);        init();        for(int i = 0; i < m; i++)            addEdge(a[i], b[i], 1, c[i]);        dijkstra(s, dist1);        init();        for(int i = 0; i < m; i++)            addEdge(b[i], a[i], 1, c[i]);        dijkstra(t, dist2);        init();        for(int i = 0; i < m; i++) {            int u = a[i];            int v = b[i];            int w = c[i];            if(dist1[u]+dist2[v]+w == dist1[t]) {                addEdge(u, v, 1, w);            }        }        int ans = dinic();        printf("%d\n", ans);    }    return 0;}
阅读全文
0 0