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里面有很聪明的人,他们总是可以用最小的代价来阻断AB的路径。

城市A里面有很强的技工,他们可以在{2N-1}里面任选两点ab新建一条ab的边或者重新改造原图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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值