bzoj2285: [Sdoi2011]保密 二分答案 分数规划 最小割

bzoj2285: [Sdoi2011]

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

分析

首先这肯定是一道最小点覆盖的题。
至于点权的话要用分数规划处理。
首先处理分数规划问题。
首先二分一个mid
考虑答案ans

nitinisi=ans

于是有
inti=ansinsi

intianssi=0

我们把每条边的边权重置为 tianssi
那么当存在一条路径到当前点使得当前点的最短路小于零的时候答案就可以再小,否则要更大。
二分答案后对于每个点得到点权,按照最小点覆盖的方式建图即可。

代码

/**************************************************************
    Problem: 2285
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:1776 ms
    Memory:13000 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int N = 110000;
const int M = 440000;
const double inf = 1e9;
const double eps = 1e-8;
const double EPS = 1e-3;
int s, t, gap[N], h[N];

int read() {
    char ch = getchar();int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f; 
}
int pre[N], cur[N], to[M], nxt[M], Q[N], T[N], S[N], top, n, m, n1, m1;
double w[M], d[N], val[N];
bool vis[N];
void add(int u, int v, double ww = 0) {to[++top] = v; w[top] = ww; nxt[top] = pre[u]; pre[u] = top;}
void adds(int u, int v, double w) {add(u, v, w); add(v, u, 0);}

bool bfs() {
    for(int i = 0;i <= t; ++i) h[i] = -1;
    ++gap[++h[t]]; int head = 0, tail;
    Q[tail = 1] = t;
    while(head < tail) {
        int u = Q[++head]; 
        for(int i = pre[u]; i; i = nxt[i]) 
        if(!(~h[to[i]])) {
            ++gap[h[to[i]] = h[u] + 1];
            Q[++tail] = to[i];
        }
    }
    return ~h[s];
}

double dfs(int u, double minf) {
    if(u == t) return minf;
    double flow = 0, f;
    for(int i = cur[u]; i; i = nxt[i])
        if(fabs(w[i]) > eps && h[u] == h[to[i]] + 1) {
            f = min(w[i], minf - flow);
            f = dfs(to[i], f); flow += f;
            w[i] -= f; w[i ^ 1] += f;
            if(fabs(w[i]) > eps) cur[u] = i;
            if(flow == minf) return flow;
        }
    if(!(--gap[h[u]])) h[s] = t + 2;
    ++gap[++h[u]];
    cur[u] = pre[u];
    return flow;
}

double sap() {
    if(!bfs()) return 0; double ans = 0;
    for(int i = 1;i <= t; ++i) cur[i] = pre[i];
    while(h[s] < t + 2) ans += dfs(s, inf);
    return ans;
}

bool check(double mid, int t) {
    for(int i = 1;i <= m; ++i) w[i] = 1.0 * T[i] - S[i] * mid; 
    for(int i = 1;i <= n; ++i) d[i] = inf, vis[i] = 0;
    int head = 0, tail; Q[tail = 1] = n; d[n] = 0; vis[n] = 1;
    while(head < tail) {
        int u = Q[++head]; vis[u] = false;
        for(int i = pre[u]; i; i = nxt[i]) 
        if(d[to[i]] > d[u] + w[i] + eps) {
            d[to[i]] = d[u] + w[i];
            if(to[i] == t && d[t] < eps) return 1;
            if(!vis[to[i]]) {vis[to[i]] = true; Q[++tail] = to[i];}
        }
    } 
    return d[t] < eps;
}

void work(int u) {
    double l = 0, r = 10.0, ans = inf;
    while(r - l > EPS) {
        double mid = (l + r) / 2;
        if(check(mid, u)) {r = mid - eps; ans = mid;}
        else l = mid + eps;
    }
    val[u] = ans;
}

int main() {
    n = read(); m = read();
    for(int i = 1;i <= m; ++i) {
        int a = read(), b = read(); T[i] = read(), S[i] = read();
        add(a, b);
    }
    m1 = read(); n1 = read();
    for(int i = 1;i <= n1; ++i)  work(i); s = n + 1; t = s + 1;
    top = 1; for(int i = 0; i <= t; ++i) pre[i] = 0;
    for(int i = 1;i <= n1; ++i) 
        if(i & 1) adds(s, i, val[i]);
        else adds(i, t, val[i]);
    for(int i = 1;i <= m1; ++i) {
        int u = read(), v = read();
        adds(u, v, inf);
    }
    double ans = sap();
    if(ans + eps > inf) puts("-1");
    else printf("%.1lf\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值