二元关系最小割

来源:互联网 发布:阿里妈妈淘宝客安全吗 编辑:程序博客网 时间:2024/05/17 05:56

Description

高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。
  作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。
Solution

因为只有两种选择,并且选择之间有关系,我们自然想到割。

因为每个割将所有点分成两个集合,总喜悦值减去这个割就是答案,显然就是最小割。

重点在于,如何连边?

暂时先考虑只有两个点x,y的情况,设源点为S选文科,汇点为T选理科。

a表示选文科的喜悦值,b表示选理科的喜悦值,cx,y表示x,y都选文科的值,dx,y表示都选理科,W表示边权
这里写图片描述
分四种情况讨论。

x,y都选文

那么割掉W(x,T)+W(y,T),失去的喜悦值就是bx+by+dx,y
x,y都选理

那么割掉W(S,x)+W(S,y),失去喜悦值ax+ay+cx,y
x选文,y选理

割掉W(x,T)+W(S,y)+W(x,y),失去喜悦值bx+ay+cx,y+dx,y
x选理,y选文

割掉W(S,x)+W(y,T)+W(x,y),失去喜悦值ax+by+cx,y+dx,y
写在一起
W(x,T)+W(y,T)=bx+by+dx,y
W(S,x)+W(S,y)=ax+ay+cx,y
W(x,T)+W(S,y)+W(x,y)=bx+ay+cx,y+dx,y
W(S,x)+W(y,T)+W(x,y)=ax+by+cx,y+dx,y
明显看出
W(S,x)=ax+cx,y2
W(S,y)=ay+cx,y2
W(x,T)=bx+dx,y2
W(y,T)=by+dx,y2
W(x,y)=cx,y+dx,y2
那么对于多个,我们推广。
W(S,x)=ax+cx,x所有朋友2
W(x,T)=bx+dx,x所有朋友2
W(x,y)=cx,y+dx,y2
然后根据最大流=最小割,跑一遍dinic或者GAP就好了
————————————————————————————
题意:Y国有N座城市,并且有M条双向公路将这些城市连接起来,并且任意两个城市至少有一条路径可以互达。
Y国的国王去世之后,他的两个儿子A和B都想成为新的国王,但他们都想让这个国家更加安定,不会用武力解决问题。
于是他们想将这个国家分成两个小国家A国和B国。现在,A拥有1号城市,B拥有N号城市,其他的城市还尚未确定归属哪边(划分之后的国家内部城市可以不连通)。
由于大家都想让国家变得更好,而某些城市的人民愿意国王的A儿子作为他们的领袖,而某些城市更看好B,而为了交通的便捷,如果划分后的公路连接两个同一个国家的城市,那么更利于城市之间的交流。于是大臣们设计了一种对土地划分的评分机制,具体如下:
1. 对于城市i,如果它划分给A国,将得到VA[i]的得分;划分给B国,将得到VB[i]的得分。
2. 对于一条公路i,如果它连接两个A国的城市,将得到EA[i]的得分;连接两个B国的城市,将得到EB[i]的得分;否则,这条公路将失去意义,将扣除EC[i]的得分。
现请你找到最优的土地划分,使得这种它的评分最高。

很明显的二元关系的最小割,新建S,T,对于每个点,由S向I连VA[I],由I向T连VB[I].对于每条边,由S向x,y连EA/2,由T向x,y连EB/2.x,y互连EA/2+EB/2+EC,特别地,S向1连INF,N向T连INF.我们先将所有可能的收益加起来,网络流中的边权代表扣除的收益,这样跑最小割之后就是剩余的最大收益。
Tips:先将所有权值乘上2,最后除以2,这样避免实数运算。

  scanf("%d%d",&n,&m);s=0,t=n+1;st[1]=INF;ed[n]=INF;  for(int i=2;i<=n-1;i++)  {    int x;scanf("%d",&x);    st[i]+=2*x;    ans+=2*x;  }  for(int i=2;i<=n-1;i++)  {    int x;scanf("%d",&x);    ed[i]+=2*x;ans+=2*x;  }  for(int i=1;i<=m;i++)  {    int x,y,ea,eb,ec;    scanf("%d%d%d%d%d",&x,&y,&ea,&eb,&ec);    ans+=(ea+eb)*2;st[x]+=ea;st[y]+=ea;ed[x]+=eb;ed[y]+=eb;    addedge(x,y,ea+eb+2*ec);addedge(y,x,ea+eb+2*ec);  }  for(int i=1;i<=n;i++)   {    addedge(s,i,st[i]);    addedge(i,t,ed[i]);  }  printf("%d\n",(ans-maxflow())/2);
原创粉丝点击