[BZOJ]2285: [Sdoi2011]保密 01分数规划+spfa+最小割

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国人之前。

题解:

国庆的时候做的……当时无限TLE,今天才AC……这题很坑,第一次输入n、m,第二次输入m1、n1……然后又调了一下eps,才AC……
下面大概说一下做法:就是01分数规划+spfa先求出n到每个出入口的最小危险值,然后就是经典的最小割模型了,对于每个空腔的入口u,出口v,u到连一条流量为inf的边,然后对于1~n1的每个点i,若为入口(奇数),st到i连一条流量为f[i]的边,f[i]表示n到i的最小危险值,若为出口(偶数),i到ed连一条流量为f[i]的边,跑最小割就好了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const double eps=1e-7;
const int Maxn=800;
const double inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int h[Maxn],st,ed,n,m,n1,m1;
struct Edge{int y,next;double d;}e[210000];
struct Edge1{int y,s,t,next;}e1[100010];
int len=1,last[Maxn];
void ins(int x,int y,double d)
{
    int t=++len;
    e[t].y=y;e[t].d=d;
    e[t].next=last[x];last[x]=t;
}
int len1=0,last1[Maxn];
void ins1(int x,int y,int s,int t)
{
    int t1=++len1;
    e1[t1].y=y;e1[t1].s=s;e1[t1].t=t;
    e1[t1].next=last1[x];last1[x]=t1;
}
void addedge(int x,int y,double d){ins(x,y,d);ins(y,x,0);}
bool bfs()
{
    queue<int>q;
    memset(h,0,sizeof(h));h[st]=1;
    q.push(st);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=last[x];i;i=e[i].next)
        if(e[i].d>eps&&!h[e[i].y])h[e[i].y]=h[x]+1,q.push(e[i].y);
    }
    return h[ed];
}
double dfs(int x,double f)
{
    if(x==ed)return f;
    double s=0.0,t;
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(h[y]==h[x]+1&&e[i].d>eps&&s<f)
        {
            t=dfs(y,min(f-s,e[i].d));
            s+=t;e[i^1].d+=t;e[i].d-=t;
        }
    }
    if(s<=eps)h[x]=0;
    return s;
}
double f[Maxn],dis[Maxn];
bool in[Maxn];
bool spfa(int Ed,double v)
{
    memset(in,false,sizeof(in));
    for(int i=1;i<=n;i++)dis[i]=inf;dis[n]=0.0;
    queue<int>q;
    q.push(n);
    while(!q.empty())
    {
        int x=q.front();q.pop();in[x]=false;
        for(int i=last1[x];i;i=e1[i].next)
        {
            int y=e1[i].y;double d=(double)(e1[i].t)-(double)(e1[i].s)*v;
            if(dis[x]+d<dis[y])
            {
                dis[y]=dis[x]+d;
                if(dis[Ed]<=eps)return true;
                if(!in[y])in[y]=true,q.push(y);
            }
        }
    }return dis[Ed]<=eps;
}
bool same(double x,double y){return abs(x-y)<=eps;}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int a=read(),b=read(),t=read(),s=read();
        ins1(a,b,s,t);
    }
    m1=read();n1=read();
    for(int i=1;i<=n1;i++)
    {
        double l=0.0,r=10.0;
        while(r-l>eps)
        {
            double mid=(l+r)/2.0;
            if(spfa(i,mid))r=mid-eps;
            else l=mid+eps;
        }
        if(same(dis[i],inf))f[i]=inf;
        else f[i]=r+eps;
    }
    st=n1+1;ed=st+1;
    for(int i=1;i<=m1;i++)
    {
        int u=read(),v=read();
        addedge(u,v,inf);
        if(same(f[u],inf)&&same(f[v],inf)){puts("-1");return 0;}
    }
    for(int i=1;i<=n1;i++)
    if(i&1)addedge(st,i,f[i]);
    else addedge(i,ed,f[i]);
    double ans=0.0;
    while(bfs())ans+=dfs(st,inf);
    printf("%.1lf",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值