洛谷2494 bzoj2285 SDOI2011保密 分数规划 最短路 最小割

题目:

现在,保密成为一个很重要也很困难的问题。如果没有做好,后果是严重的。比如,有个人没有自己去修电脑,又没有拆硬盘,后来的事大家都知道了。
当然,对保密最需求的当然是军方,其次才是像那个人。为了应付现在天上飞来飞去的卫星,军事基地一般都会建造在地下。
某K国的军事基地是这样子的:地面上两排大天井共n1个作为出入口,内部是许多除可以共享出入口外互不连通的空腔,每个空腔有且只有两个出入口,并且这两个出入口不会在同一排。为了方便起见,两排出入口分别编号为1,3,5…和2,4,6…并且最大的编号为n1。
虽然上面扯了那么多关于保密的东西,但是其实解密也是一件很纠结的事情。但其实最简单直接暴力无脑的解密方法就是找个人去看看。。。
我们有很牛X的特种部队,只需要派出一支特种部队到K国基地的某个出入口,那么和这个出入口直接相连的所有空腔都可以被探索,但也只有这些空腔可以被这支部队探索。现在有足够多的特种部队可以供你调遣,你必须使用他们调查完所有的K国基地内的空腔。
当然,你的基地离K国基地不会太近,周边的地图将会给你,表示为n个检查点和m条连接这些点的道路,其中点1到点n1就是K国基地的出入口,点n是你的部队的出发点。对每条道路,有不同的通行时间t和安全系数s。因为情报部门只对单向的道路安全系数进行了评估,所以这些道路只允许单向通行,并且不会存在环。
一支特种部队从你的基地出发,通过某条路径,到达某个K国基地出入口,此时这支部队的危险性表示为总时间和这条路径经过的所有道路的安全系数和的比值。整个行动的危险性表示为你派出的所有部队的危险性之和。你需要使这个值最小的情况下探索整个K国基地。
快点完成这个任务,在K国的叫兽宣布你是K国人之前。

题解:
首先求出1号点到所有入口的最短路,这个最短路需要分数规划。
之后就是把每个点的两个出口一个向源点连边,一个向汇点连边,流量为1号点到该点的最短路长度。
由于每个点我们只会选择其中一条路去走,于是就相当于一个最小割。
注意精度函数的写法。
下面是代码
#include 
using namespace std;

int n,m,hed[710],cnt,m1,n1,inq[710],q[200001],h,t,st,ed;
int dep[710];
double dis[710],eps=1e-3,c[200001],d[710],res;
struct node
{
	int from,to,next;
	double safe,t;
}e[200001],b[200001];
struct gg
{
	int to,next;
	double c;
}a[401000];

int jingdu(double x)
{
	if(x<=eps&&x>=-eps)
	return 0;
	if(x>0)
	return 1;
	else
	return -1;
}
void add(int from,int to)
{
	b[++cnt].from=from;
	b[cnt].to=to;
	b[cnt].next=hed[from];
	hed[from]=cnt;
}
void ins(int from,int to,double c)
{
	a[++cnt].to=to;
	a[cnt].c=c;
	a[cnt].next=hed[from];
	hed[from]=cnt;
	a[++cnt].to=from;
	a[cnt].c=0;
	a[cnt].next=hed[to];
	hed[to]=cnt;
}
void spfa(int id)
{
	memset(d,127,sizeof(d));
	memset(inq,0,sizeof(inq));
	inq[n]=1;
	q[1]=n;
	d[n]=0;
	h=1;t=2;
	while(h!=t)
	{
		int x=q[h];
		inq[x]=0;
		for(int i=hed[x];i;i=b[i].next)
		{
			int y=b[i].to;
			if(jingdu(d[y]-d[x]-c[i])>0)
			{
				d[y]=d[x]+c[i];
	//到id的最短路有负权边直接退出			
				if(y==id&&jingdu(d[y])<=0) 
				return;
				if(!inq[y])
				{
					q[t++]=y;
					inq[y]=1;
				}
			}
		}
		h++;
	}
}
int check(int id,double mid)
{
	for(int i=1;i<=m;i++)
	c[i]=e[i].t-e[i].safe*mid;
	spfa(id);
	if(jingdu(d[id])<=0)
	return 1;
	else
	return 0;
}
double find(int id)
{
	double l=0,r=10,mid,ji=2e9;
	while(r-l>eps)
	{
		mid=(l+r)/2.0;
		if(check(id,mid))
		ji=r=mid;
		else
		l=mid;
	}
	return ji;
}
int bfs()
{
	memset(dep,0,sizeof(dep));
	q[1]=st;
	h=1;t=2;
	dep[st]=1;
	while(h!=t)
	{
		int x=q[h];
		for(int i=hed[x];i;i=a[i].next)
		{
			int y=a[i].to;
			if(jingdu(a[i].c)&&dep[y]==0)
			{
				dep[y]=dep[x]+1;
				q[t++]=y;
			}
		}
		h++;
	}
	if(dep[ed]>0)
	return 1;
	else
	return 0;
}
double flow(int x,double f)
{
	if(x==ed||!jingdu(f))
	return f;
	double s=0,t;
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(dep[y]==dep[x]+1&&jingdu(a[i].c))
		{
			s+=(t=flow(y,min(a[i].c,f-s)));
			a[i].c-=t;
			a[i^1].c+=t;
			f-=t;
			if(!jingdu(f))
			break;
		}
	} 
	if(!jingdu(s))
	dep[x]=0;
	return s;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%lf%lf",&e[i].from,&e[i].to,&e[i].t,&e[i].safe);
		add(e[i].from,e[i].to);
	}
	scanf("%d%d",&m1,&n1);
	for(int i=1;i<=n1;i++)
	dis[i]=find(i);
	st=n+1,ed=n+2;
	cnt=1;
	memset(hed,0,sizeof(hed));
	for(int i=1;i<=n1;i++)
	{
		if(i&1)
		ins(st,i,dis[i]);
		else
		ins(i,ed,dis[i]);		
	}
	for(int i=1;i<=m1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ins(x,y,2e9);
	}
	while(bfs())
	res+=flow(st,2e9);
	if(res>1e8)
	printf("-1\n");
	else
	printf("%.1lf\n",res);
	return 0;
}



你可能感兴趣的:(最小割,最短路,图论,网络流,分数规划)