洛谷 P1345 [USACO5.4]奶牛的电信Telecowmunication

来源:互联网 发布:超级记忆训练软件 编辑:程序博客网 时间:2024/04/30 08:11

洛谷 P1345 [USACO5.4]奶牛的电信Telecowmunication

//P1345 [USACO5.4]奶牛的电信Telecowmunication
//通过该题,学习了最大流,做了模板题  P3376 【模板】网络最大流
//http://www.cnblogs.com/albert7xie/p/5323591.html思路介绍得不错,摘抄如下:
/*我记得以前貌似是删边的。删点其实也类似,把点转换成边就可以了。所以就要拆点了,拆点的方法不多说。
然后跑一遍最小割(最大流),原来的边的流量设为无限大,拆点的边就置为1,不过要注意的是这里是无向边。
主要问题就是怎么让答案的字典序最小。其实枚举就行了!枚举每一个点,然后删点,跑最大流,看一下流量是否变小,变小了就真(是真的!)删掉这个点,如果没有改变说明删不删这个点都一样。
因为每次增广都肯定会少掉至少一条边(点),这里的求最大流速度就会很快,大概是O(N2),那么总的时间复杂度就是O(N3)。
*/
//http://www.cnblogs.com/txhwind/archive/2012/08/18/2645333.html题解也写得不错,摘抄如下:
/*这个问题是要求该图的最小点割集,可以使用最小割最大流定理。
但是最小割是边的割,该怎么办呢?
拆点!将每个点拆成入点和出点。所有入边连到入点上,出边从出点连出,流量均为+INF。再在入点和出点间连一条流量为1的边。然后做最大流即为答案。
至于输出最小字典序方案的问题,可以参考milk6,从小到大枚举删点,若删去后流量减少了1,则输出。
*/
//学习了Ford-Fulkerson算法 EdmondsKarp算法 Dinic算法 发现这个题解写得最不错
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P1345摘抄如下:
//这题难在建模啊
//~~不知道光说怎么连边不说原因的网络流题解有什么意义~~
//不难看出,这道题是求最小割点集的大小。
//显然的是,对于一个点,它只能被删一次。~~废话~~
//那么,对于每一个点i,我们都要复制它(设为i+n),并且从i到i+n连1的边(因为只能删一次)。(反向连0不要忘记)
//add ( i, i+n, 1 ) ; add ( i+n, i, 0 ) ;
//然后怎么看待原图中本来就存在的边呢?它们只是有一个联通的作用,对于流量并没有限制,所以明确一点:这些边加入网络中限制应该为无限大。
//假设现在要从原图中添加一条从x到y的有向边(这道题是无向边,再依下面的方法添加一个y到x的就行了)到网络中去,对于点y来说,这条边的加入不应该影响通过它的流量限制(就是前面连的那个1)发生变化,所以前面那条y到y+n的边应该接在这条边的后面,所以这条边的终点连向网络中的y,相反的,这条边应该受到x的(前面连的1)限制。因为,假设x已经被删(x到x+n满流),那么这条边再加不加都是没有变化的。所以,这条边在网络中的起点应该是x+n,这样才保证受到限制。
//add ( y+n, x, 无限大 ) ;
//add ( x, y+n, 0 ) ;
//add ( x+n, y, 无限大 ) ;
//add ( y, x+n, 0 ) ;
//不要忘记反向的。
//http://www.fx114.net/qa-255-116191.aspx读后有启发,摘抄如下:
//将每个点拆分为两个点(如i拆分为i1和i2),原图中的边(i, j)在新图中变成四条有向边:(i1, i2) = 1,(j1, j2) = 1,(i2, j1) = INFINITY,(j2, i1) = INFINITY,等号后面为边权
//源点为c1的第二个点c12,汇点为c2的第一个点c21,求最大流MaxFlow(c12, c21),则为第一问的答案
//遍历每一条边(i1, i2),i = [1, N]并且c1, c2除外。如果将其去掉后当前最大流减少了1,则代表点i属于最小割集,也就是第二问的答案中
//http://www.cnblogs.com/SilverNebula/p/6062798.html读后有启发,摘抄如下:
//网络流最小点割。把每个点拆成入点和出点(i->i+n),在入点和出点之间连一条容量为1的边。将所给m条边容量设为INF,跑最大流即可。
//http://www.cnblogs.com/Currier/p/6695444.html读后有启发,摘抄如下:
//把一个点拆成两个点,连上容量为1的边,然后把其他边连一连求一下最小割就好了。
//刚开始求c1的入点到c2的出点的最小割了,WA了一发,其实是求c1的出点到c2的汇点的最小割。要注意。
//http://www.wosoni.com/cnblogs-modengdubai-p-4865569.html介绍构图上,讲解不错,摘抄如下:
//读完题就知道是求两点独立轨条数的问题。
//就是将一个点拆成两个点求最大流。
//但是在如何将一个点拆分成两个点之后如何建图的问题上纠结了好久。
//其实就是将一个点拆分成流入点和流出点。然后流入到流出之间有一条容量为1的有向弧。
//原来的a和b之间联通的话,就是a的出点流到了b的入点,且b的出点流到了a的入点。
//还有一个问题要解决,就是有多个最小割时如何得到集合大小最小,且字典序最小的集合。
//其实因为拆分之后的流入点到流出点之间的容量为1,且原来每个点拆分之后的自己的流入和流出之间的容量都为1.
//可以证明所有的最小割的集合大小相等。所以只要找到字典序最小的那个就好了。
//根据退流定理,枚举删除每个点。
//先找到编号最小的一个割点。
//然后删除这个点,求得删除这个点之后的最大流。将容量网络恢复。并在容量网络中删除这个点。一直重复这一步直到容量网络的最大流变为0.
//有了上面的基础,该题弄明白了。关键字:入点,出点。
附上该题最核心的图:


//提交,测试点1,2,10RE,明白数组开得太小。
//修改 e[600*4+100+100];//2此处写成 e[600*2+100+100];提交,测试点1,2RE
//将数组中的100统统变成1000,提交AC,真是火大,精确算出的数组容量尽然不对
//仔细想想,确实不对,拆点,1变2.
//继续修改 ,int n,m,s,t,head[100*2+10],cnt=1,q[100*2+10],level[100*2+10],maxflow=0;//8 head[1000+10],cnt=1,q[1000+10],level[1000+10],maxflow=0;//7 数组容量100+10 ,
//提交AC
//该题,注意,bfs,dfs,都是从s+n到t 即出点到入点
#include <stdio.h>
#include <string.h>
#define INF 999999999
struct node{
    int to,next,c;
}e[600*4+100*2+100];//3此处写成e[600*4+100+100] // 2此处写成 e[600*2+100+100];
int n,m,s,t,head[100*2+10],cnt=1,q[100*2+10],level[100*2+10],maxflow=0;//8 head[1000+10],cnt=1,q[1000+10],level[1000+10],maxflow=0;//7 数组容量100+10
int min(int a,int b){
    return a>b?b:a;
}
void add(int u,int v,int c){//邻接表
    cnt++,e[cnt].to=v,e[cnt].next=head[u],e[cnt].c=c,head[u]=cnt;
}
int bfs(int s1,int t1){//6一个重大问题,队尾用变量t,终点也用变量t,显然不行//分层
    int u,v,h,tail,b;//b边
    memset(level,0,sizeof(level));//每次分层前,初始化
    h=tail=1;
    q[tail]=s1;
    level[s1]=1;
    tail++;
    while(h<tail){
        u=q[h];
        h++;
        for(b=head[u];b>1;b=e[b].next){
            v=e[b].to;
            if(!level[v]&&e[b].c){
                level[v]=level[u]+1;
                q[tail]=v;
                tail++;
            }
        }
    }
    return level[t1]>0;
}
int dfs(int now,int t1,int limit){//增广
    int u,v,b,flow=0,f;
    if(now==t1||!limit)return limit;
    for(b=head[now];b>1;b=e[b].next){
        v=e[b].to;
        if(level[now]+1==level[v]&&(f=dfs(v,t1,min(limit,e[b].c)))){
            flow+=f;
            limit-=f;
            e[b].c-=f;
            e[b^1].c+=f;
        }
        if(!limit)break;
    }
    return flow;
}
int main(){
    int i,x,y;
    memset(head,0,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(i=1;i<=m;i++){
        scanf("%d%d",&x,&y);//1 重大失误,此处写成 scanf("%d%d%d",&x,&y);
        add(x+n,y,INF);
        add(y,x+n,0);
        add(y+n,x,INF);
        add(x,y+n,0);
    }
    for(i=1;i<=n;i++){
        add(i,i+n,1);
        add(i+n,i,0);
    }
    while(bfs(s+n,t))
        maxflow+=dfs(s+n,t,INF);
    printf("%d\n",maxflow);
    return 0;
}

2017-7-2 16:56

原创粉丝点击