您的位置:首页 > 其它

hdoj 3987 Harry Potter and the Forbidden Forest 【求所有最小割里面 最少的边数】

2015-08-30 11:57 465 查看

Harry Potter and the Forbidden Forest

Time Limit: 5000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 1802 Accepted Submission(s): 602



Problem Description

Harry Potter notices some Death Eaters try to slip into Castle. The Death Eaters hide in the most depths of Forbidden Forest. Harry need stop them as soon as.



The Forbidden Forest is mysterious. It consists of N nodes numbered from 0 to N-1. All of Death Eaters stay in the node numbered 0. The position of Castle is node n-1. The nodes connected by some roads. Harry need block some roads by magic and he want to minimize
the cost. But it’s not enough, Harry want to know how many roads are blocked at least.


Input

Input consists of several test cases.

The first line is number of test case.

Each test case, the first line contains two integers n, m, which means the number of nodes and edges of the graph. Each node is numbered 0 to n-1.

Following m lines contains information about edges. Each line has four integers u, v, c, d. The first two integers mean two endpoints of the edges. The third one is cost of block the edge. The fourth one means directed (d = 0) or undirected (d = 1).

Technical Specification

1. 2 <= n <= 1000

2. 0 <= m <= 100000

3. 0 <= u, v <= n-1

4. 0 < c <= 1000000

5. 0 <= d <= 1



Output

For each test case:

Output the case number and the answer of how many roads are blocked at least.



Sample Input

3

4 5
0 1 3 0
0 2 1 0
1 2 1 1
1 3 1 1
2 3 3 1

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

3 6
0 1 1 0
0 1 2 0
1 1 1 1
1 2 1 0
1 2 1 0
2 1 1 1




Sample Output

Case 1: 3
Case 2: 2
Case 3: 2




题意:有N个点(编号从0到N-1)和M条边,边的信息有四个——起点、终点、破坏该边需要的花费、d,其中当d为1时说明边是双向的,d为0时说明是单向边。问你在花费最小的前提下,阻断0和N-1需要破坏的最少边数。



分析:

1,在原图最小割不唯一的前提下,第一次求出的最小割的边数未必是最少的。在割边集的边权和相等的前提下,可能存在一个边数更少的最小割。

2,不管有多少个最小割,我们在原图跑一次最大流之后,残量网络里面满流的边一定是属于某个或多个最小割的,相应的没有满流的边一定不属于任何一个最小割。

3,这样问题就变成——在所有满流的边中破坏最少的边数来阻断0到N-1的路径,类似在最短路的边中破坏最少的边来阻断起点到终点的路径,只是多了对非最短路边(在本题中是非满流边)的处理。

思路:

1,先建图,在原图跑一次0到N-1的最大流。

2,以残量网络为基础构建新图。遍历所有正向弧,若满流则更改边权为1,否则边权为无穷大,注意对反向弧要初始化边的信息。

3,最后在新图求一次0到N-1的最小割,就是答案。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define MAXN 1010
#define MAXM 400000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], cur[MAXN], edgenum;
int dist[MAXN];
bool vis[MAXN];
int N, M;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
void getMap()
{
    int a, b, c, d;
    while(M--)
    {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        a++, b++;
        addEdge(a, b, c);
        if(d)
            addEdge(b, a, c);
    }
}
bool BFS(int s, int t)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow)
            {
                dist[E.to] = dist[u] + 1;
                if(E.to == t) return true;
                vis[E.to] = true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
int DFS(int x, int a, int t)
{
    if(x == t || a == 0) return a;
    int flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap - E.flow), t)) > 0)
        {
            edge[i].flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Maxflow(int s, int t)
{
    int flow = 0;
    while(BFS(s, t))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(s, INF, t);
    }
    return flow;
}
int k = 1;
void solve()
{
    Maxflow(1, N);
    //对残量网络 进行处理
    for(int i = 0; i < edgenum; i+=2)
    {
        Edge E = edge[i];
        if(E.cap == E.flow)//满流的边 改变边权->1
        {
            edge[i].cap = 1;
            edge[i].flow = 0;
        }
        else
        {
            edge[i].cap = INF;
            edge[i].flow = 0;
        }
        edge[i^1].cap = edge[i^1].flow = 0;//处理反向边
    }
    printf("Case %d: %d\n", k++, Maxflow(1, N));//再求一次最小割就是答案
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &N, &M);
        init();
        getMap();
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: