[P2494][SDOI2011]保密(整体二分+拓扑排序+最大流)

这道题其实分为两步:1.求出从点n到点1~n1的最短距离,也就是ΣSi/ΣTi(i为路径上的点)的最小值。  2.求出能到达所有空腔的距离之和的最小值。  我们逐个来考虑。

对于第一步,我们可以看到每个点的答案都在0.1到10.0的范围内,考虑整体二分。(然后发现蒟蒻不会整体二分,赶快临时学习一下…)如果某个点的最短距离小于等于当前尝试的解,即有ΣSi/ΣTi<=x,整理后即为ΣSi-ΣTi*x<=0。所以我们每次把边权设为s-t*x,求从n点出发到1~n1点的最短路。注意到题中保证没有环,所以只需要先求一个拓扑序,每次按序更新就好了。

对于第二步,要距离和最小,我们联想到最小割问题。由于每个空腔只要有一个出口可以到达就行,且它的两个出口一定是一奇一偶,所以我们把每个空腔的奇数口向偶数口连一条容量为inf的边(图不连通时,这条边无流量,即代表这个空腔已经被访问啦~)。然后从源点向奇数点连边,偶数点向汇点连边,容量为第一步求的最小距离。只要有一个空腔没被访问,他的两个出口和源点或汇点的连边就同时都没有断掉,图就是联通的,所以最小割即是答案。由于最大流=最小割的定理,我们只要跑一边最大流就完成了√

涉及double的题就习惯性设个eps吧w

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=710,M=100010,inf=1e7;
const double eps=1e-3;
struct edge1{
	int y,t,s,next;
}data1[M];
struct edge{
	int y,next;
	double f;
}data[M];
int n,m,n1,m1,s,t,num,num1,num2,rnk[N],h1[N],d[N],h[N],dep[N],cur[N],q1[N],q2[N];
double a1[N],dis[N];
queue<int> q;
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void addedge1(int u,int v,int s1,int t1){
	data1[++num1].y=v,data1[num1].s=s1,data1[num1].t=t1,data1[num1].next=h1[u],h1[u]=num1;
}
inline void addedge(int u,int v,double f){
	data[++num].y=v,data[num].f=f,data[num].next=h[u],h[u]=num;
	data[++num].y=u,data[num].f=0,data[num].next=h[v],h[v]=num;
}
inline double min1(double x,double y){
	return x<y?x:y;
}
inline void topo(double x){
	for(int i=1;i<n;++i)a1[i]=1.0*inf;a1[n]=0.0;
	for(int j=1,u;j<=n;++j){
		u=rnk[j];
		for(int i=h1[u],v;i!=-1;i=data1[i].next){
			v=data1[i].y;
			double w=a1[u]+data1[i].t-data1[i].s*x;
			if(a1[v]>w)a1[v]=w;
		}
	}
}
void f1(double l,double r,int a,int b){
	if(a>b)return;
	if(fabs(r-l)<=eps){for(int i=a;i<=b;++i)dis[q1[i]]=l;return;}
	double mid=(l+r)/2;
	topo(mid);
	int qa=a-1,qb=b+1;
	for(int i=a;i<=b;++i)
		if(a1[q1[i]]<=0.0)q2[++qa]=q1[i];
		else q2[--qb]=q1[i];
	for(int i=a;i<=b;++i)q1[i]=q2[i];
	f1(l,mid,a,qa);f1(mid+eps,r,qb,b);
}
inline bool bfs(int s,int t){
	for(int i=1;i<=t;++i)cur[i]=h[i],dep[i]=0;
	while(!q.empty())q.pop();
	dep[s]=1,q.push(s);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=h[u],v;i!=-1;i=data[i].next){
			v=data[i].y;
			if(!dep[v]&&fabs(data[i].f)>=eps)dep[v]=dep[u]+1,q.push(v);
		}
	}
	return dep[t];
}
double dfs(int u,int t,double lim){
	if(u==t)return lim;
	double res=0;
	for(int& i=cur[u],v;i!=-1;i=data[i].next){
		v=data[i].y;
		if(dep[v]==dep[u]+1&&fabs(data[i].f)>=eps){
			double fmax=dfs(v,t,min1(data[i].f,lim-res));
			if(fabs(fmax)>=eps){
				data[i].f-=fmax,data[i^1].f+=fmax,res+=fmax;
				if(fabs(res-lim)<=eps)return res;
			}
		}
	}
	return res;
}
inline double dinic(int s,int t){
	double ans=0;
	while(bfs(s,t))ans+=dfs(s,t,inf*1.0);
	return ans;
}
int main(){
	n=read(),m=read();
	num1=0,memset(h1,-1,sizeof h1),memset(d,0,sizeof d);
	for(int i=1,u,v,s1,t1;i<=m;++i)u=read(),v=read(),t1=read(),s1=read(),addedge1(u,v,s1,t1),++d[v];
	while(!q.empty())q.pop();num2=0;
	for(int i=1;i<=n;++i)if(!d[i])q.push(i);
	while(!q.empty()){
		rnk[++num2]=q.front();q.pop();
		for(int i=h1[rnk[num2]],v;i!=-1;i=data1[i].next){
			v=data1[i].y;--d[v];
			if(!d[v])q.push(v);
		}
	}
	for(int i=1;i<n;++i)q1[i]=i;dis[n]=0;
	f1(0.0,11.0,1,n-1);
	for(int i=1;i<=n;++i)if(dis[i]-10.1>eps)dis[i]=inf*1.0;
	m1=read(),n1=read();
	num=-1;memset(h,-1,sizeof h);
	for(int i=1,x1,x2;i<=m1;++i){
		x1=read(),x2=read();
		if(dis[x1]>11.0&&dis[x2]>11.0){printf("-1");return 0;}
		addedge(x1,x2,inf*1.0);
	}
	s=n1+1,t=n1+2;
	for(int i=1;i<=n1;++i)
		if(i&1)addedge(s,i,dis[i]);
		else addedge(i,t,dis[i]);
	printf("%.1lf",dinic(s,t));
	return 0;
} 

 

拓扑排序和最小成本最大流(Minimum Cost Maximum Flow, MCMP)是图论中的两个概念,它们可以结合起来解决某些优化问题。 首先,拓扑排序是一种将有向无环图(DAG, Directed Acyclic Graph)中的顶点按照依赖关系线性排列的方式,即对于每条有向边 (u, v),节点 u 的排列位置总是在节点 v 之前。它常用于任务调度、课程安排等场景,确定活动执行的顺序。 其次,最小成本最大流是指在一个网络中寻找从源点到汇点的最大流量路径,并使得该路径的成本(如费用或时间)最小。这种算法通常用在物流、资源分配等问题上,比如找到成本最低的运输路线。 两者结合的情况通常是这样的:如果图中不仅包含流量容量限制,还有边上的费用(例如,每单位流量经过某条边需要支付一定的费用),我们可以先通过拓扑排序确定一个合理的路径顺序。然后,在这个顺序的基础上应用最小成本最大流算法,找出在满足所有依赖关系的前提下,成本最小的流量分配方案。 举个例子,假设有一个工程项目,每个任务都有依赖关系(拓扑排序),并且完成每个任务需要的时间和成本不同(最小成本最大流)。我们想要确定一个最经济高效的执行顺序,这时就可以先做拓扑排序得到任务执行的基本路径,再用MCMP计算出在这条路径上如何分配资源以达到最小成本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值