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

题目链接: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;
// Dinic
int 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;
}
// Dijkstra
bool 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值