• [bzoj2285][Sdoi2011]保密【0/1分数规划】【网络流】


    【题目描述】

    Description

    现在,保密成为一个很重要也很困难的问题。如果没有做好,后果是严重的。比如,有个人没有自己去修电脑,又没有拆硬盘,后来的事大家都知道了。
    当然,对保密最需求的当然是军方,其次才是像那个人。为了应付现在天上飞来飞去的卫星,军事基地一般都会建造在地下。
    某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国人之前。

    Input

    第一行2个正整数n,m (4 <= n <= 700, m <= 100000) 表示整个地区地图上的检查点和道路数。
    下面m行,每行4个正整数a, b, t, s(a, b <=n, 1 <= t, s <= 10)表示一条从a到b的道路需时为t,安全系数为s。
    接下来1行2个正整数m1和n1(m1 <= 40000, n1 < min{n, 161}), m1表示K国基地空腔的个数,n1表示K国基地出入口的个数。
    再接下来m1行,每行2个正整数u, v (u, v<=n1, u是奇数,v是偶数),表示每个空腔的2个出入口。 

    Output

    一行,最小的危险性,保留一位小数。或者输出”-1”(无引号)表示此任务不可能完成。

    Sample Input

    5 5
    5 1 10 1
    5 1 10 1
    5 2 9 1
    5 3 7 1
    5 4 8 1
    4 4
    1 2
    1 4
    3 2
    3 4

    Sample Output

    17.0

    HINT


    n1<=20

    Source

    【题解】

     这道题题意挺繁琐的,要求的东西也有些多,但思路挺简单的。

     首先0/1分数规划求出n到每个出入口的最优代价(二分+spfa),接下来就变成了一个最小覆盖问题。

     新建一个原点S和汇点T,S向奇数点连边,流量为到这个点的代价,偶数点向T连边,流量也为这个点的代价。对于一个基地的

    两个出(u,v),在它们中连一条流量为inf的边,跑出的最小割即为答案。

    /* --------------
        user Vanisher
        problem bzoj-2285
    ----------------*/
    # include <bits/stdc++.h>
    # define 	ll 		long long
    # define 	N 		710
    # define 	M 		200100
    # define 	inf 	1e9
    # define 	eps 	1e-6
    using namespace std;
    int read(){
    	int tmp=0, fh=1; char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    	return tmp*fh;
    }
    struct Edge{
    	int data,next;
    	double a,b;
    }ee[M*2];
    struct Flow{
    	int data,next,re;
    	double l;
    }fe[M*2];
    int eplace,fplace,fhead[N],ehead[N],n,use[N],q[N],S,T,now[N],n1,m1,m,flag;
    double dis[N],ans; 
    void buildedge(int u, int v, double a, double b){
    	ee[++eplace].data=v; ee[eplace].next=ehead[u]; ehead[u]=eplace; ee[eplace].a=a; ee[eplace].b=b;
    }
    void buildflow(int u, int v, double l){
    	fe[++fplace].data=v; fe[fplace].next=fhead[u]; fhead[u]=fplace; fe[fplace].l=l; fe[fplace].re=fplace+1;
    	fe[++fplace].data=u; fe[fplace].next=fhead[v]; fhead[v]=fplace; fe[fplace].l=0; fe[fplace].re=fplace-1;
    }
    void spfa(int pd, double p){
    	for (int i=1; i<=n; i++) dis[i]=inf;
    	memset(use,0,sizeof(use));
    	dis[n]=0; use[n]=true; int pl=1, pr=1; q[1]=n;
    	while (pl<=pr){
    		int x=q[(pl++)%n];
    		for (int ed=ehead[x]; ed!=0; ed=ee[ed].next)
    			if (dis[ee[ed].data]>dis[x]+ee[ed].a-ee[ed].b*p){
    				dis[ee[ed].data]=dis[x]+ee[ed].a-ee[ed].b*p;
    				if (use[ee[ed].data]==false)
    					use[ee[ed].data]=true, q[(++pr)%n]=ee[ed].data;
    			}
    		use[x]=false;
    		if (dis[pd]<=0) return;
    	}
    }
    double getdis(int x){
    	double pl=0, pr=10.010, now=inf;
    	while (pl+eps<=pr){
    		double mid=(pl+pr)/2;
    		spfa(x,mid);
    		if (dis[x]<=0) 
    			now=mid, pr=mid-eps;
    			else pl=mid+eps;
    	}
    	return now;
    }
    void bfs(){
    	for (int i=1; i<=T; i++) dis[i]=inf;
    	int pl=1, pr=1; q[1]=S;
    	while (pl<=pr){
    		int x=q[pl++];
    		for (int ed=fhead[x]; ed!=0; ed=fe[ed].next)
    			if (dis[fe[ed].data]==inf&&fe[ed].l>=eps)
    				dis[fe[ed].data]=dis[x]+1, q[++pr]=fe[ed].data;
    	}
    }
    double dfs(int x, double flow){
    	if (x==T) return flow;
    	double sum=0;
    	for (int ed=now[x]; ed!=0; ed=fe[ed].next)
    		if (fe[ed].l>=eps&&dis[fe[ed].data]==dis[x]+1){
    			double tmp=dfs(fe[ed].data,min(flow,fe[ed].l));
    			sum=sum+tmp; flow=flow-tmp;
    			fe[ed].l-=tmp; fe[fe[ed].re].l+=tmp;
    			if (flow<eps){
    				now[x]=ed;
    				return sum;
    			}
    		}
    	now[x]=0;
    	return sum;
    }
    double dinic(){
    	double sum=0;
    	for (bfs(); dis[T]!=inf; bfs()){
    		for (int i=S; i<=T; i++) now[i]=fhead[i];
    		sum=sum+dfs(S,inf);
    	}
    	return sum;
    }
    int main(){
    	n=read(), m=read();
    	for (int i=1; i<=m; i++){
    		int u=read(), v=read(), a=read(), b=read();
    		buildedge(u,v,a,b);
    	}
    	m1=read(), n1=read(); S=0; T=n1+1;
    	for (int i=1; i<=n1; i++)
    		if (i%2==1) buildflow(S,i,getdis(i));
    			else buildflow(i,T,getdis(i));
    	for (int i=1; i<=m1; i++){
    		int l=read(), r=read();
    		buildflow(l,r,inf);
    	}
    	ans=dinic();
    	if (ans>=inf)
    		printf("-1
    ");
    		else printf("%.1lf",ans);
    	return 0;
    }






  • 相关阅读:
    整除
    奇怪的生日礼物
    欧拉函数平方和
    奇怪的生日礼物(数论基础)
    整除(简单数论)
    Tarjan求割点
    构造双连通(tarjan)
    次小生成树
    机器扫边
    最短路径(树形DP)
  • 原文地址:https://www.cnblogs.com/Vanisher/p/9136031.html
Copyright © 2020-2023  润新知