【UVA1389】Hard Life(分数规划+网络流)

点此看题面

  • 给定一张\(n\)个点\(m\)条边的无向图,求最大密度子图(即边数除以点数最大的导出子图)。
  • \(n\le100,m\le1000\)

分数规划

这种问题一看就是分数规划。

我们直接二分答案\(x\),则\(\frac EP\ge x\)就可以写成\(E-x\times P\ge 0\)。

即选择每条边的价值是\(1\),每个点的价值是\(-x\)。

最大权闭合子图

考虑只有选了两个端点,才能选择这条边,是一个经典的最大权闭合子图模型。

注意有可能不选任何点会取得最优价值,因此我们最大流之后需要加上判断选择的点数不为\(0\)。

至于如何判断一个点是否被选择,根据最大权闭合子图模型的建图方式,断开与超级汇相连的边表示选择,那么此时就与超级源连通。即从超级源出发存在一条到该点的增广路径,也就是最后一次\(BFS\)时的\(dep\not=-1\)。

\(O(Dinic)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
#define M 1000
#define INF 1e9
#define DB double
using namespace std;
int n,m;struct line {int x,y;}p[M+5];
namespace Dinic
{
	#define PS (N+M+2)
	#define ES (N+3*M)
	#define s (n+m+1)
	#define t (n+m+2)
	#define E(x) ((((x)-1)^1)+1)
	#define add(x,y,f) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].F=f)
	int ee,lnk[PS+5],cur[PS+5];struct edge {int to,nxt;DB F;}e[2*ES+5];
	I void Cl() {ee=0;for(RI i=1;i<=t;++i) lnk[i]=0;}
	I void Add(CI x,CI y,Con DB& f) {add(x,y,f),add(y,x,0);}
	int q[PS+5],D[PS+5];I bool BFS()
	{
		RI i,k,H=1,T=1;for(i=1;i<=t;++i) D[i]=-1;D[q[1]=s]=0;
		W(H<=T&&!~D[t]) for(i=lnk[k=q[H++]];i;i=e[i].nxt)
			e[i].F&&!~D[e[i].to]&&(D[q[++T]=e[i].to]=D[k]+1);return ~D[t];
	}
	I DB DFS(CI x=s,DB f=INF)
	{
		if(x==t||!f) return f;RI i;DB o,g=0;for(i=cur[x];i;i=e[i].nxt)
		{
			if(D[e[i].to]^(D[x]+1)||!(o=DFS(e[i].to,min(f,e[i].F)))) continue;
			if(g+=o,e[i].F-=o,e[E(i)].F+=o,!(f-=o)) break;
		}return cur[x]=i,g;
	}
	I DB MaxFlow(Con DB& x)//最大权闭合子图模型
	{
		RI i;for(Cl(),i=1;i<=n;++i) Add(i,t,x);//点向超级汇
		for(i=1;i<=m;++i) Add(s,n+i,1),Add(n+i,p[i].x,INF),Add(n+i,p[i].y,INF);//超级源向边,边向点
		DB f=0;W(BFS()) memcpy(cur,lnk,sizeof(cur)),f+=DFS();return f;//最大流
	}
	I bool Check(CI x) {return ~D[x];}//判断是否选择(是否与超级源连通)
}
int main()
{
	RI i,c;DB l,r,mid,res;W(scanf("%d%d",&n,&m)==2)
	{
		for(i=1;i<=m;++i) scanf("%d%d",&p[i].x,&p[i].y);
		if(!m) {puts("1\n1\n");continue;}//特判没有边的情况
		l=0,r=m;W(r-l>1e-6) {res=m-Dinic::MaxFlow(mid=(l+r)/2);//分数规划
			for(i=1;i<=n;++i) if(Dinic::Check(i)) break;i<=n&&res>0?l=mid:r=mid;}//必须有点被选择
		for(Dinic::MaxFlow(l),c=0,i=1;i<=n;++i) Dinic::Check(i)&&++c;printf("%d\n",c);
		for(i=1;i<=n;++i) Dinic::Check(i)&&printf("%d\n",i);putchar('\n');
	}return 0;
}
上一篇:创建链接服务器


下一篇:Kubernetes环境Traefik部署与应用