MENU

HDU3416 Marriage Match IV(最短路+最大流)

2018 年 09 月 08 日 • 阅读: 2114 • 图论阅读设置

Marriage Match IV

  • Time Limit: 2000/1000 MS (Java/Others)
  • Memory Limit: 32768/32768 K (Java/Others)
  • Total Submission(s): 5663
  • Accepted Submission(s): 1655

Problem Description

Do not sincere non-interference。
Like that show, now starvae also take part in a show, but it take place between city A and B. Starvae is in city A and girls are in city B. Every time starvae can get to city B and make a data with a girl he likes. But there are two problems with it, one is starvae must get to B within least time, it's said that he must take a shortest path. Other is no road can be taken more than once. While the city starvae passed away can been taken more than once.
So, under a good RP, starvae may have many chances to get to city B. But he don't know how many chances at most he can make a data with the girl he likes . Could you help starvae?

Input

The first line is an integer T indicating the case number.(1<=T<=65)
For each case,there are two integer n and m in the first line ( 2<=n<=1000, 0<=m<=100000 ) ,n is the number of the city and m is the number of the roads.

Then follows m line ,each line have three integers a,b,c,(1<=a,b<=n,0<c<=1000)it means there is a road from a to b and it's distance is c, while there may have no road from b to a. There may have a road from a to a,but you can ignore it. If there are two roads from a to b, they are different.

At last is a line with two integer A and B(1<=A,B<=N,A!=B), means the number of city A and city B.
There may be some blank line between each case.

Output

Output a line with a integer, means the chances starvae can get at most.

Sample Input

3
7 8
1 2 1
1 3 1
2 4 1
3 4 1
4 5 1
4 6 1
5 7 1
6 7 1
1 7

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

2 2
1 2 1
1 2 2
1 2

Sample Output

2
1
1

Author

starvae@HDU

Source

HDOJ Monthly Contest – 2010.06.05

链接

http://acm.hdu.edu.cn/showproblem.php?pid=3416

题意

n个点m条边带权有向图,问每条边最多只能走一次时从A到B的最短路径数量。

题解

因为限制了每条边最多只能走一次,所以不能直接用最短路来搞。但是我们可以转化为网络流,每条边只能走一次,所以可以设置边的容量为1。当然,首先需要找到在最短路上的边,可以通过两遍最短路求得,然后用这些边跑最大流得到的就是答案。

  • dijkstra(S, dist1); // 跑S到所有点的最短路
  • dijkstra(T, dist2); // 跑T到所有点的最短路
  • dist1[a[i]] + c[i] + dist2[b[i]] == dist1[T] // 最短路上的边

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1010;
const int maxm = 200010;
int dist1[maxn], dist2[maxn];
bool vis[maxn];
int n, m, N;

struct Node
{
    int v, c;
    Node(int _v, int _c): v(_v), c(_c) {}
    bool operator < (const Node & r) const
    {
        return c > r.c;
    }
};

struct Edge
{
    int to, cost;
    Edge(int _to, int _cost): to(_to), cost(_cost) {}
};

vector <Edge> edge[maxn];

void addedge(int u, int v, int w) // 最短路加边
{
    edge[u].push_back(Edge(v, w));
}

void dijkstra(int s, int dist[]) // 最短路
{
    for (int i = 1; i <= N; ++i)
    {
        dist[i] = inf;
        vis[i] = false;
    }
    dist[s] = 0;
    priority_queue <Node> Q;
    Q.push(Node(s, dist[s]));
    while (!Q.empty())
    {
        Node tmp = Q.top();
        Q.pop();
        int u = tmp.v;
        if (vis[u])
            continue;
        vis[u] = true;
        for (int i = 0; i < edge[u].size(); ++i)
        {
            int v = edge[u][i].to, cost = edge[u][i].cost;
            if (!vis[v] && dist[v] > dist[u] + cost)
            {
                dist[v] = dist[u] + cost;
                Q.push(Node(v, dist[v]));
            }
        }
    }
}

struct Edges
{
    int u, v, cap;
    Edges() {}
    Edges(int u, int v, int cap): u(u), v(v), cap(cap) {}
} es[maxm];
int R, S, T;
vector <int> tab[maxn]; // 边集
int dis[maxn];
int current[maxn];

void addedges(int u, int v, int cap) // 最大流加边
{
    tab[u].push_back(R);
    es[R++] = Edges(u, v, cap); // 正向边
    tab[v].push_back(R);
    es[R++] = Edges(v, u, 0); // 反向边容量为0
    // 正向边下标通过异或就得到反向边下标, 2 ^ 1 == 3 ; 3 ^ 1 == 2
}

int BFS()
{
    queue <int> q;
    q.push(S);
    memset(dis, 0x3f, sizeof(dis));
    dis[S] = 0;
    while (!q.empty())
    {
        int h = q.front();
        q.pop();
        for (int i = 0; i < tab[h].size(); i++)
        {
            Edges &e = es[tab[h][i]];
            if (e.cap > 0 && dis[e.v] == 0x3f3f3f3f)
            {
                dis[e.v] = dis[h] + 1;
                q.push(e.v);
            }
        }
    }
    return dis[T] < 0x3f3f3f3f; // 返回是否能够到达汇点
}

int dinic(int x, int maxflow)
{
    if (x == T)
        return maxflow;
    // i = current[x] 当前弧优化
    for (int i = current[x]; i < tab[x].size(); i++)
    {
        current[x] = i;
        Edges &e = es[tab[x][i]];
        if (dis[e.v] == dis[x] + 1 && e.cap > 0)
        {
            int flow = dinic(e.v, min(maxflow, e.cap));
            if (flow)
            {
                e.cap -= flow; // 正向边流量降低
                es[tab[x][i] ^ 1].cap += flow; // 反向边流量增加
                return flow;
            }
        }
    }
    return 0; // 找不到增广路 退出
}

int DINIC()
{
    int ans = 0;
    while (BFS()) // 建立分层图
    {
        int flow;
        memset(current, 0, sizeof(current)); // BFS后应当清空当前弧数组
        while (flow = dinic(S, 0x3f3f3f3f)) // 一次BFS可以进行多次增广
            ans += flow;
    }
    return ans;
}

int a[maxm], b[maxm], c[maxm];

int main()
{
    int kase;
    scanf("%d", &kase);
    while (kase--)
    {
        scanf("%d%d", &n, &m);
        N = n;
        for (int i = 0; i <= N; ++i) // 清空
            edge[i].clear();
        for (int i = 1; i <= m; ++i) // 先正向加边
        {
            scanf("%d%d%d", &a[i], &b[i], &c[i]);
            addedge(a[i], b[i], c[i]);
        }
        scanf("%d%d", &S, &T);
        dijkstra(S, dist1); // 跑S到所有点的最短路
        for (int i = 0; i <= N; ++i) // 清空
            edge[i].clear();
        for (int i = 1; i <= m; ++i) // 反向加边
            addedge(b[i], a[i], c[i]);
        dijkstra(T, dist2); // 跑T到所有点的最短路
        R = 0;
        for (int i = 0; i <= N; i++)
            tab[i].clear();
        for (int i = 1; i <= m; ++i)
        {
            if (a[i] != b[i] && dist1[a[i]] + c[i] + dist2[b[i]] == dist1[T]) // 最短路上的边
                addedges(a[i], b[i], 1);
        }
        int ans = DINIC(); // 求最大流
        printf("%d\n", ans);
    }
    return 0;
}

The end.
2018-09-08 星期六
最后编辑于: 2018 年 10 月 24 日