zoukankan      html  css  js  c++  java
  • hdu3251 最小割

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3251

    最大流最小割定理:s-t之间的最大流一定等于s-t之间的最小割的容量。最小割就是割除的正向边的最小容量之和使得源与漏之间不连通。

    本题给出n个点,m条有向边,还有f个可用点,要求一个边割集使得s-t之间无流量,花费就是f个点的点权减去割边的总长度,也就是割边的花费。只需要用超级汇连接所给出f个点,然后用dinic算法求一次s-t最大流,最后一次增广的时候无法到达汇点,所以此时S能到达的点属于S集,其他点都是T能到达的点,也就构成了S-T点割集,将顶点在S集中,终点在T集中的边输出即时结果。

    这道题有一个注意点就是为什么给出的f个点连向汇点的的边权是等于他们的点权的?我的解释是这样的,我们在最初把这些点的点权都加起来了,设置为sum,那么对于一个可选点a与汇点的连边e,如果dinic算法结束的时候该边的边权是0的话就说明这个点是不可取的,这条边就是割除的边中的一条,所以这个点的权是得不到的。如果边权大于零的话就说明这个点是源点无法到达的,所以也就是能选择的。所以如果要问选择的点是哪些的话可以扫描一遍这些边权来确定。其实,也可以从这个角度得知,割就是使得某条边的边权或者说容量为0。

    代码如下:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 typedef unsigned int ui;
      4 typedef long long ll;
      5 typedef unsigned long long ull;
      6 #define pf printf
      7 #define mem(a,b) memset(a,b,sizeof(a))
      8 #define prime1 1e9+7
      9 #define prime2 1e9+9
     10 #define pi 3.14159265
     11 #define lson l,mid,rt<<1
     12 #define rson mid+1,r,rt<<1|1
     13 #define scand(x) scanf("%llf",&x) 
     14 #define f(i,a,b) for(int i=a;i<=b;i++)
     15 #define scan(a) scanf("%d",&a)
     16 #define mp(a,b) make_pair((a),(b))
     17 #define P pair<int,int>
     18 #define dbg(args) cout<<#args<<":"<<args<<endl;
     19 #define inf 0x7ffffff
     20 inline int read(){
     21     int ans=0,w=1;
     22     char ch=getchar();
     23     while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
     24     while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
     25     return ans*w;
     26 }
     27 const int maxn=1e5+10;
     28 int n,m;
     29 int eu[maxn*10],ev[maxn*10],ew[maxn*10],num[maxn*10];
     30 int t;
     31 struct flow{
     32     int s,t;
     33     int head[maxn],nxt[maxn*10],d[maxn],cur[maxn];//cur当前弧优化 
     34     bool vis[maxn];
     35     struct node{
     36         int u,v,w;
     37     }p[maxn*10];
     38     int e;
     39     void init()
     40     {
     41         e=0;
     42         mem(head,-1);
     43         mem(nxt,-1);
     44     }
     45     void addedge(int u,int v,int w)//顺带添加反向边 
     46     {
     47         p[e].u=u;
     48         p[e].v=v;
     49         p[e].w=w;
     50         nxt[e]=head[u];
     51         head[u]=e++;
     52         p[e].u=v;
     53         p[e].v=u;
     54         p[e].w=0;
     55         nxt[e]=head[v];
     56         head[v]=e++; 
     57     }
     58     bool bfs(int src,int sink)//确定bfs序 
     59     {
     60         mem(vis,0);
     61         mem(d,0);
     62         d[src]=1;
     63         vis[src]=1;
     64         queue<int> q;
     65         q.push(src);
     66         while(!q.empty())
     67         {
     68             int cur=q.front();
     69             q.pop();
     70             for(int i=head[cur];~i;i=nxt[i])
     71             {
     72                 int v=p[i].v;
     73                 if(!vis[v]&&p[i].w)//确定这个点没有被标号,并且不是反向边 
     74                 {
     75                     vis[v]=1;
     76                     d[v]=d[cur]+1;
     77                     q.push(v);
     78                 }
     79             }
     80         }
     81         if(d[sink])return true;
     82         return false;
     83     }
     84     int dfs(int s,int flow)
     85     {
     86         if(s==t)return flow;
     87         int used=0;
     88         for(int i=head[s];~i;i=nxt[i])
     89         {
     90             int v=p[i].v,w=p[i].w;
     91             if(d[v]==d[s]+1&&w>0)//根据Dinic算法的思想,只能走正向的、bfs序大一的边 
     92             {
     93                 int tmp=dfs(v,min(flow-used,w));
     94                 if(tmp>0)
     95                 {
     96                     p[i].w-=tmp;//更新正向边的流量以及反向边的流量, 
     97                     p[i^1].w+=tmp;//正向边是偶数,它对应的反向边就是正向边+1
     98                     used+=tmp;//从一个点出发最多的流量是flow,用掉的流量需要更新
     99                     if(used==flow)break; 
    100                  } 
    101             }
    102         }
    103         if(!used)d[s]=0;//从该点出发的流不能被使用,所以这个点在这次搜索中被丢弃 
    104         return used;
    105     }
    106     int dinic()
    107     {
    108         int ans=0;
    109         while(bfs(s,t))
    110         {
    111 //            memcpy(cur,head,sizeof(head));
    112             ans+=dfs(s,inf);
    113         }
    114         return ans;
    115     }
    116 }a;
    117 int main()
    118 {
    119     //freopen("input.txt","r",stdin);
    120     //freopen("output.txt","w",stdout);
    121     std::ios::sync_with_stdio(false);
    122     t=read();
    123     int f;
    124     int k=0;
    125     while(t--)
    126     {
    127         int u,v,w;
    128         a.init(); 
    129         n=read(),m=read(),f=read();
    130         a.s=1;a.t=n+1;
    131         f(i,1,m)
    132         {
    133             u=eu[i]=read(),v=ev[i]=read(),w=ew[i]=read();//保存的只有正向边 
    134             a.addedge(u,v,w); 
    135         }
    136         int co=0;
    137         f(i,1,f)
    138         {
    139             u=read(),v=read();
    140             co+=v;
    141             a.addedge(u,a.t,v);//将选中点与汇点连边,边权为城市的价值 
    142         }
    143         int ans=a.dinic();
    144         pf("Case %d: %d
    ",++k,co-ans);
    145         int cnt=0;
    146         f(i,1,m)//扫描所有的正向边 
    147         {
    148             if(a.vis[eu[i]]&&!a.vis[ev[i]])
    149             { 
    150                 num[cnt++]=i;
    151             }
    152         }
    153         pf("%d",cnt);
    154         f(i,0,cnt-1)pf(" %d",num[i]);
    155         pf("
    ");
    156      } 
    157 } 
    每一个不曾起舞的日子,都是对生命的辜负。
  • 相关阅读:
    OAuth2.0介绍
    C#集成FastDFS断点续传
    ABP集成WIF实现单点登录
    CentOS7下搭建FastDfs(V5.11)+Keepalived分布式集群部署
    【算法】之常见的排序算法
    【开发工具
    【C/C++】之C/C++快速入门
    【数据结构】之二叉树(Java语言描述)
    【数据结构】之散列链表(Java语言描述)
    【数据结构】之队列(Java语言描述)
  • 原文地址:https://www.cnblogs.com/randy-lo/p/12593990.html
Copyright © 2011-2022 走看看