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 } 
每一个不曾起舞的日子,都是对生命的辜负。
原文地址:https://www.cnblogs.com/randy-lo/p/12593990.html