hdoj 2435 There is a war 【求原图最小割已经分成的两个点集 + 枚举两点集里面的点建新边 求残量网络的最大最小割】

There is a war

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 993    Accepted Submission(s): 283

Problem Description

There is a sea.

There are N islands in the sea.

There are some directional bridges connecting these islands.

There is a country called Country One located in Island 1.

There is another country called Country Another located in Island N.

There is a war against Country Another, which launched by Country One.

There is a strategy which can help Country Another to defend this war by destroying the bridges for the purpose of making Island 1 and Island n disconnected.

There are some different destroying costs of the bridges.

There is a prophet in Country Another who is clever enough to find the minimum total destroying costs to achieve the strategy.

There is an architecture in Country One who is capable enough to rebuild a bridge to make it unbeatable or build a new invincible directional bridge between any two countries from the subset of island 2 to island n-1.

There is not enough time for Country One, so it can only build one new bridge, or rebuild one existing bridge before the Country Another starts destroying, or do nothing if happy.

There is a problem: Country One wants to maximize the minimum total destroying costs Country Another needed to achieve the strategy by making the best choice. Then what’s the maximum possible result?

Input

There are multiple cases in this problem.

There is a line with an integer telling you the number of cases at the beginning.

The are two numbers in the first line of every case, N(4<=N<=100) and M(0<=M<=n*(n-1)/2), indicating the number of islands and the number of bridges.

There are M lines following, each one of which contains three integers a, b and c, with 1<=a, b<=N and 1<=c<=10000, meaning that there is a directional bridge from a to b with c being the destroying cost.

There are no two lines containing the same a and b.

Output

There is one line with one integer for each test case, telling the maximun possible result.

Sample Input

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

Sample Output

0
2
1
3

题意:有N个岛屿和M条连接岛屿的有向边。已知在岛屿1上有一个城市A,在岛屿N上有一个城市B。现在城市A要攻打城市B,城市B为了保护自己,决定阻断城市A到自己的路径。

城市B里面有很聪明的人,他们总是可以用最小的代价来阻断A到B的路径。

城市A里面有很强的技工,他们可以在{2—N-1}里面任选两点a、b新建一条a到b的边或者重新改造原图M条边里面的一条边 使得新边无法被破坏。为了让城市B付出最大的代价,他们一定会选择最优的建边方案。

问你:城市B需要付出的最大代价。

分析:最小割一定会把原图分成两个点集。一个是源点能到达的S集,一个是能到达汇点的T集(可达是针对残量网络而言)。设点a属于S集,点b属于T集,那么最优的建边方案一定是建a -> b的边。

实现过程

1,求出原图的最小割ans,;

2,在残量网络里求出S集和T集;

3,枚举两集合里面的点,在残量网络里面建一条新边求最小割。得到最大的最小割need;

4,最后结果就是ans+need。

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define MAXN 110
#define MAXM 20000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM], Redge[MAXM];
int head[MAXN], Rhead[MAXN], edgenum, Redgenum, cur[MAXN];
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++;
}
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 Sr[MAXN], Tr[MAXN];//记录S集 T集里面的点
int S, T;
void find_S(int u)
{
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        Edge E = edge[i];
        if(vis[E.to]) continue;
        if(E.cap > E.flow)
        {
            vis[E.to] = true;
            Sr[S++] = E.to;
            find_S(E.to);
        }
    }
}
void solve()
{
    init();
    int a, b, c;
    for(int i = 0; i < M; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        addEdge(a, b, c);
    }
    int ans = Maxflow(1, N);//最小割
    memset(vis, false, sizeof(vis));
    S = 0;
    vis[1] = true;//1不能再访问
    find_S(1);
    T = 0;
    for(int i = 2; i <= N-1; i++)
        if(!vis[i]) Tr[T++] = i;//记录T集里面的点
    memcpy(Rhead, head, sizeof(head));
    memcpy(Redge, edge, sizeof(edge));
    Redgenum = edgenum;
    int need = 0;//额外代价
    for(int i = 0; i < S; i++)
    {
        for(int j = 0; j < T; j++)
        {
            memcpy(head, Rhead, sizeof(Rhead));
            memcpy(edge, Redge, sizeof(Redge));
            edgenum = Redgenum;
            addEdge(Sr[i], Tr[j], INF);//建新边
            need = max(need, Maxflow(1, N));
        }
    }
    printf("%d\n", ans+need);
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &N, &M);
        solve();
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-05 11:17:35

hdoj 2435 There is a war 【求原图最小割已经分成的两个点集 + 枚举两点集里面的点建新边 求残量网络的最大最小割】的相关文章

hdoj 4612 Warm up【双连通分量求桥&amp;&amp;缩点建新图求树的直径】

Warm up Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 5093    Accepted Submission(s): 1131 Problem Description N planets are connected by M bidirectional channels that allow instant transport

HDU 2435 There is a war (网络流-最小割)

There is a war Problem Description There is a sea. There are N islands in the sea. There are some directional bridges connecting these islands. There is a country called Country One located in Island 1. There is another country called Country Another

HDU 2435 There is a war Dinic 最小割

题意是有n座城市,n号城市不想让1号城市可达n号,每条道路有一条毁坏的代价,1号还可以修一条不能毁坏的道路,求n号城市所需的最小代价最大是多少. 毁坏的最小代价就直接求一遍最大流,就是最小割了.而可以修一条不能毁坏的路,需要枚举的这些边就是源集中的点到汇集中的点,我之前的做法是直接找出所有的割边,再枚举每一条割边,在这两个点上加一个流量为无穷大的边再去求最大流,实际上这样连样例的第二个数据都过不了,因为图不保证是连通的,那就有一些源集中的点和汇集中的点本来就不联通,就不存在这样的割边了.发现这个

评委打分问题---去掉两个最高分,两个最低分,求平均分

//评委打分 去掉两个最高分 去掉两个最低分 求平均数 int []defen={78,79,80,83,89,99,90,76,88,98}; for(int a=0;a<defen.length;a++) { System.out.print(defen[a]+"\t"); } System.out.println();//第一步,初始化 for(int b=1;b<defen.length-1;b++) { for(int a=0;a<defen.length-

HDU 2435 There is a war(修改或添加一条边的最小割 )经典

There is a war Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 952    Accepted Submission(s): 269 Problem Description There is a sea. There are N islands in the sea. There are some directional

【HDOJ】2809 God of War

状态DP. 1 /* 2809 */ 2 #include <iostream> 3 #include <queue> 4 #include <cstdio> 5 #include <cstring> 6 #include <cstdlib> 7 #include <algorithm> 8 using namespace std; 9 10 #define MAXN 20 11 12 typedef struct { 13 int

网络战争 [KD-Tree+最小割树]

题面 思路 首先吐槽一下: 这题是什么东西啊??出题人啊,故意拼题很有意思吗??还拼两个这么毒瘤的东西???? 10K代码了解一下???? 然后是正经东西 首先,本题可以理解为这样: 给定$n$个块,每个块有一个根,每个根只会主动连出去一条无向边,每次求两点最小割 那么,我们显然可以把每个块内的最小割树建立出来,同时把块的根之间的最小割树也建立出来 如果询问点在同一个块里面,显然可以直接最小割树处理 否则就是两边的点到块根的最小割和两个块根之间的最小割的最小值 所以,我们先对于所有的块根,建出K

算法-求两个有序数组两两相加的值最小的K个数

我的思路是: 用队列,  从(0,0)開始入队,每次出队的时候,选(1,0) (0,1) 之间最小的入队,假设是相等的都入队,假设入过队的就不入了,把出队的k个不同的输出来就可以 我測试了几组数据都是对的.可是可能还是会有BUG,或者我忽略的地方.以下是我的实现代码(假设有错,请大家积极指正) import java.util.LinkedList; import java.util.Queue; /** * 有两个序列 A 和 B,A=(a1,a2,...,ak),B=(b1,b2,...,b

有一分数序列: 2/1 3/2 5/3 8/5 13/8 21/13...... 求出这个数列的前N项之和,保留两位小数。

题目描述 输入 N 输出 数列前N项和 样例输入 10 样例输出 16.48 代码:#include<stdio.h>int main(){    int i,N;    double c=0,a=2.0,b=1.0,t,sum=0;         scanf("%d",&N);    for(i=0;i<N;i++)    {           c=a/b;        sum=sum+c;        t=a;        a=a+b;