网络流强化-UVA10480

  做这道题,自己先是想了好几种找被割的边的方法——都被否决了。

  后来发现是最小割:只要一条边的两端在不同的点集里面就代表是被割掉的满流边(这些满流边的流量和等于最大流的流量与最小割的权值和)。

  但是之前自己想了一个例子,

  

10 11
1 4 3
4 3 3
3 2 3
4 5 100
5 6 100
6 7 100
7 2 100
1 8  100
8 9  100
9 10 100
10 3 100
0 0

 

  首先这个例子我自己误判了,以为最大流的流量是6,只要割掉1-4和3-2就行——就是脑子短路了,所以我还反过来怀疑双向边,再怀疑DFS和BFS寻找割集的方法,最后居然怀疑最大流和最小割的关系。

  后来用了别人的代码,发现我自己解出来的最大流量值错了;但是用自己编的数据(在上面的基础上修改的):

  

10 11
1 4 3
4 3 3
3 2 3
4 5 3
5 6 100
6 7 100
7 2 100
1 8  100
8 9  100
9 10 100
10 3 100
0 0

  她居然给我跑出来了三条容量为3的边,这就是为什么我怀疑最大流和最小割的关系的原因,后来发现人家在寻找源点能到的点集的时候,不仅要求边的剩余流量大于0,还要求基础的cap容量要大于0,这样的做法是错的。修改之后就好了。

  AC代码:

 

//http://www.renfei.org/blog/isap.html 带解释的ISAP
//https://www.cnblogs.com/bosswnx/p/10353301.html 形式和我的比较相近的 ISAP
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxe 2048  //pay  双向边 一共10万条路 双向就是20万 反边就是40万
#define maxv 55    //pay
#define maxn 205    //pay
#define sc scanf
#define pt printf
#define rep(i,a,b)  for(int i=(a);i<(b);++i)
const int inf = 0x3f3f3f3f; 
int cg,sp,ins;  //cg change sp是总流量 ins是加速回溯点
int N,M ,s,t;
int q[maxv],fro,rea;
typedef struct ed{
    int v,nxt,cap; //dis
}ed;
ed e[maxe];
typedef struct cz{
    int x,y; //dis
}cz;
cz ob[maxe];
int tp;
int tot,head[maxv],cur[maxv],vis[maxv],bk[maxv],d[maxv],num[maxv]; //
int mi(int a,int b) {return a<b?a:b;}
int mx(int a,int b) {return a>b?a:b;}
void add(int u,int v,int cap)
{
    e[tot].v=v;         e[tot].nxt=head[u];
    /*e[tot].dis=dis;*/     e[tot].cap=cap;
    head[u]=tot++;

    e[tot].v=u;         e[tot].nxt=head[v];
    /*e[tot].dis=-dis;*/    e[tot].cap=0;
    head[v]=tot++;
} 
// 仅有一次的BFS为ISAP节省了不少时间 
bool bfs()
{
    //数组模拟queue
    memset(vis, 0, sizeof(vis));
    fro = rea = 0;
    q[rea] = t; ++rea;
    vis[t] = 1;
    d[t] = 0;
    int u,v,i;
    while (rea>fro) 
    {
        u = q[fro]; ++fro;
        for (i=head[u]; i!=-1; i=e[i].nxt) 
        {
            v=e[i].v;
            if (!vis[v] && e[i^1].cap ) 
            {
                vis[v] = true;
                d[v] = d[u] + 1;
                q[rea] = v; ++rea;
            }
        }
    }
    return vis[s];
}
// 增广
int augment()
{
    int  flow = inf, i;
    cg = t;
    // 从汇点到源点通过 p 追踪增广路径, flow 为一路上最小的残量
    while (cg != s) {
        i = bk[cg];
        if(flow>=e[i].cap)
        {
            flow = e[i].cap;
            ins = e[i^1].v;     
            //用来加速寻找,在最小流量断开的地方重新开始寻找
            //嗯,等一下 我这个是从终点往起点寻找,而确定增光路径是从起点到终点
            //那么起点是河流的上游,那么回溯的河段应该尽可能的往上游靠近
            //所以应该将flow>e[i].cap的大于号改成大于等于号
        }
        cg = e[i^1].v;
    }
    cg = t;
    // 从汇点到源点更新流量
    while (cg != s) {
        i = bk[cg];
        e[i].cap -= flow;
        e[i^1].cap += flow;
        cg = e[i^1].v;
    }
    return flow;
}
//由于每次修改层次的时候,都是在到剩下子节点的距离中挑选最短的加1 所以层次分明不会出现死循环 
int max_flow()
{
    int flow = 0,i,u,v;
    bool advanced;
    if(bfs()==false) return 0;
    memset(num, 0, sizeof(num));
    for (i = 1; i <= N; ++i) ++num[d[i]];   
    //不一定是从s到t,你要知道统计每个层次的点的个数是全局统计的
    u = s;
    memcpy(cur, head, sizeof(head));
    while (d[s] < N)        //层次问题!!! 一定要保证t是最大的 
    //终点是0,那么起点所在层次最多是N-1 同理,不是d[s]<t
    {
        if (u == t) 
        {
            flow += augment();
            u = ins;    //pay speed up
        }
        advanced = false;
        for (i = cur[u]; i!=-1; i=e[i].nxt) 
        { 
            v = e[i].v;
            if (e[i].cap && d[u] == d[v] + 1) 
            {
                advanced = true;
                bk[v] = i;
                cur[u] = i;
                u = v;
                break;
            }
        }
        if (!advanced) 
        { // retreat
            int m = N;      //层次问题!!! 一定要保证t是最大的 
            for (i = head[u]; i != -1; i=e[i].nxt)
            {
                if (e[i].cap&&m>d[e[i].v])
                {
                    cur[u] = i;
                    m = d[e[i].v];
                }
            }
            if (--num[d[u]] == 0) break; // gap 优化
            ++num[d[u] = m+1]; 
            //我以前一直在想 如果没有找到怎么办呢 现在发现原来找不到的话距离会被赋成N+1
            if (u != s)
                u = e[bk[u]^1].v;
        }
    }
    return flow;
}

void init()
{
    tot=0;
    memset(head,-1,sizeof(head));   //pay 
}
int main()
{
    freopen("in.txt","r",stdin);
    s=1, t=2,bk[0]=-1;
    while(~sc("%d%d",&N,&M)&&N&&M)
    {
        tp=0;
        sp = 0;
        int i,w;
        init();
        for(i=1;i<=M;++i)
        {
            sc("%d%d%d",&ob[tp].x,&ob[tp].y,&w);
            add(ob[tp].x,ob[tp].y,w);
            add(ob[tp].y,ob[tp].x,w);
            ++tp;
        }  
        sp = max_flow();
        //pt("%d\n",sp); 
        memset(vis, 0, sizeof(vis));
        fro = rea = 0;
        q[rea] = s; ++rea;
        vis[s] = 1;
        int u,v;
        while (rea>fro) 
        {
             u = q[fro]; ++fro;
            for (i=head[u]; i!=-1; i=e[i].nxt) 
            {
                v=e[i].v;
                if (!vis[v] && e[i].cap ) 
                {
                    vis[v] = true;
                    q[rea] = v; ++rea;
                }
            }
        }
        for(i=0;i<tp;++i) if(vis[ob[i].x]+vis[ob[i].y]==1) pt("%d %d\n",ob[i].x,ob[i].y);
        pt("\n");
    }
    return 0;
}
UVA 10480

 

转载于:https://www.cnblogs.com/lighten-up-belief/p/11362908.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值