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 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
 

Source
2008 Asia Chengdu Regional Contest Online 
题意:给一个有向图,拆掉的边的代价为边权,现在1 要到n,n 试图阻止1到达,至少花多大代价,条件是1 可以在任意两点(不含1和n)只能修改或新增一条边(此边不可被拆),求n要花费的最小代价。

解题:如果没有条件,那么就是一道最简单的最小割,但现在加一个条件 修改或新增一条边。首先求出最初的最小割,就会把n个点分成两部分:与汇点n相连,与源点相连。我们先用一个bfs标记与源点相连的那些点,那么没有被标记的点可能就是与汇点相连。
我们来分析一下:如果 修改或新增一条边的两个点要么两点都是与源点相连的点,要么都是与汇点相连,那么最小割是不会改变的,所以修改或新增的边的两个点(u,v)只能是u属于与源点相连,v与汇点相连,边权改成INF,这样才有可能使最小割变大。如果是修改存在的边,求完最小割要记得改回原来的值,如果是新增的边,那么要使边权变为0。
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define captype int

const int MAXN = 110;   //点的总数
const int MAXM = 400010;    //边的总数
const int INF = 1<<30;
struct EDG{
    int to,next;
    captype cap,flow;
} edg[MAXM];
int eid,head[MAXN];
int gap[MAXN];  //每种距离(或可认为是高度)点的个数
int dis[MAXN];  //每个点到终点eNode 的最短距离
int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边
int pre[MAXN];

void init(){
    eid=0;
    memset(head,-1,sizeof(head));
}
//有向边 三个参数,无向边4个参数
void addEdg(int u,int v,captype c,captype rc=0){
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
}
captype maxFlow_sap(int sNode,int eNode, int n){//n是包括源点和汇点的总点个数,这个一定要注意
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    pre[sNode] = -1;
    gap[0]=n;
    captype ans=0;  //最大流
    int u=sNode;
    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点
        if(u==eNode){   //找到一条可增流的路
            captype Min=INF ;
            int inser;
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min
            if(Min>edg[i].cap-edg[i].flow){
                Min=edg[i].cap-edg[i].flow;
                inser=i;
            }
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                edg[i].flow+=Min;
                edg[i^1].flow-=Min;  //可回流的边的流量
            }
            ans+=Min;
            u=edg[inser^1].to;
            continue;
        }
        bool flag = false;  //判断能否从u点出发可往相邻点流
        int v;
        for(int i=cur[u]; i!=-1; i=edg[i].next){
            v=edg[i].to;
            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){
                flag=true;
                cur[u]=pre[v]=i;
                break;
            }
        }
        if(flag){
            u=v;
            continue;
        }
        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1
        int Mind= n;
        for(int i=head[u]; i!=-1; i=edg[i].next)
        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){
            Mind=dis[edg[i].to];
            cur[u]=i;
        }
        gap[dis[u]]--;
        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径
                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流
        dis[u]=Mind+1;//如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1
        gap[dis[u]]++;
        if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边
    }
    return ans;
}
int id[MAXN][MAXN],vist[MAXN];
void bfs()
{
    queue<int>q;
    int u,v;
    memset(vist,0,sizeof(vist));
    vist[1]=1;
    q.push(1);
    while(!q.empty()) {
        u=q.front(); q.pop();
        for(int i=head[u]; i!=-1; i=edg[i].next ){
            v=edg[i].to;
            if(!vist[v]&&edg[i].cap-edg[i].flow>0)
                vist[v]=1,q.push(v);
        }
    }
}
int main()
{
    int T,n,m,u,v,c;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        init();
        memset(id,-1,sizeof(id));
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&c);
            id[u][v]=eid;
            addEdg(u,v,c);
        }
        int ans=maxFlow_sap(1,n,n);
        bfs();
        for(int i=2; i<n; i++)
        if(vist[i])
        for(int j=2; j<n; j++)
        if(!vist[j]){
            for(int ti=0; ti<eid; ti++)
                edg[ti].flow=0;
            int tans;
            if(id[i][j]!=-1){
                int tmp=edg[id[i][j]].cap;
                edg[id[i][j]].cap=INF;
                tans=maxFlow_sap(1,n,n);
                edg[id[i][j]].cap=tmp;
            }
            else{
                addEdg(i , j , INF);
                tans=maxFlow_sap(1,n,n);
                edg[eid-2].cap=0;
            }
            if(tans>ans)ans=tans;
        }
        printf("%d\n",ans);
    }

}


你可能感兴趣的:(算法,搜索,图论,网络流,最大流)