BZOJ2285 [SDOI2011]保密 【01分数规划 + 网络流】

时间:2022-04-26 17:14:22

题目

现在,保密成为一个很重要也很困难的问题。如果没有做好,后果是严重的。比如,有个人没有自己去修电脑,又没有拆硬盘,后来的事大家都知道了。

当然,对保密最需求的当然是军方,其次才是像那个人。为了应付现在天上飞来飞去的卫星,军事基地一般都会建造在地下。

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

输入格式

第一行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个出入口。

输出格式

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

输入样例

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

输出样例

17.0

题解

每个空腔有两个入口,二者必选一

显然到达每个点都是有一个固定的最优代价

显然这就是一个最小边权覆盖,用网络流即可

现在问题转化为了求\(n1\)中每个点的最小代价

比值最小,显然是\(01\)分数规划

我们二分那个最小值,用SPFA判一下是否是负的就好了

【剪枝】在SPFA过程中,如果那个点的最短路已经为负,直接返回

QAQ二分边界设错了查了半天

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define eps 1e-9
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 705,maxm = 200005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
struct FLOW{
int to[maxm],nxt[maxm],ne,h[maxn],cur[maxn],vis[maxn],d[maxn],S,T;
double f[maxm];
FLOW(){ne = 2;}
void build(int u,int v,double w){
to[ne] = v; nxt[ne] = h[u]; f[ne] = w; h[u] = ne++;
to[ne] = u; nxt[ne] = h[v]; f[ne] = 0; h[v] = ne++;
}
bool bfs(){
queue<int> q;
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
int u,t;
q.push(S); d[S] = 0; vis[S] = true;
while (!q.empty()){
u = q.front(); q.pop();
for (int k = h[u]; k; k = nxt[k]){
if (fabs(f[k]) > eps && !vis[t = to[k]]){
d[t] = d[u] + 1; vis[t] = true;
if (t == T) return true;
q.push(t);
}
}
}
return vis[T];
}
double dfs(int u,double minf){
if (fabs(minf) < eps || u == T) return minf;
int t;
double ff,flow = 0;
if (cur[u] == -1) cur[u] = h[u];
for (int& k = cur[u]; k; k = nxt[k])
if (d[t = to[k]] == d[u] + 1 && fabs(ff = dfs(t,min(minf,f[k]))) > eps){
f[k] -= ff; f[k ^ 1] += ff;
flow += ff; minf -= ff;
if (fabs(minf) < eps) break;
}
return flow;
}
double maxflow(){
double flow = 0;
while (bfs()){
for (int i = 0; i <= T; i++) cur[i] = -1;
flow += dfs(S,INF);
}
return flow;
}
}G;
int h[maxn],ne = 2;
struct EDGE{int to,nxt; double t,s;}ed[maxm];
inline void build(int u,int v,double t,double s){
ed[ne] = (EDGE){v,h[u],t,s}; h[u] = ne++;
}
int n,m,n1,m1,reach[maxn],inq[maxn];
double ri[maxn],d[maxn];
void bfs(){
queue<int> q;
q.push(n); reach[n] = true;
int u;
while (!q.empty()){
u = q.front(); q.pop();
Redge(u) if (!reach[to = ed[k].to]){
reach[to] = true;
q.push(to);
}
}
}
bool spfa(double e,int T){
queue<int> q; q.push(n);
for (int i = 1; i < n; i++) d[i] = INF,inq[i] = false;
d[n] = 0;
int u;
while (!q.empty()){
u = q.front(); q.pop();
inq[u] = false;
Redge(u) if (d[to = ed[k].to] > d[u] + (ed[k].t - e * ed[k].s)){
d[to] = d[u] + (ed[k].t - e * ed[k].s);
if (to == T && d[to] <= eps) return true;
if (!inq[to]) inq[to] = true,q.push(to);
}
}
return d[T] < eps;
}
void work1(){
for (int i = 1; i <= n1; i++){
if (!reach[i]){
ri[i] = INF;
continue;
}
double l = 0,r = 1000,mid;
while (r - l > 0.00001){
mid = (l + r) / 2;
if (spfa(mid,i)) r = mid;
else l = mid;
}
ri[i] = (l + r) / 2;
}
}
int main(){
n = read(); m = read();
int u,v,t,s;
while (m--){
u = read(); v = read(); t = read(); s = read();
build(u,v,t,s);
}
bfs();
m1 = read(); n1 = read();
work1();
G.S = 0; G.T = n1 + 1;
for (int i = 1; i <= n1; i++)
if (i & 1) G.build(G.S,i,ri[i]);
else G.build(i,G.T,ri[i]);
for (int i = 1; i <= m1; i++){
u = read(); v = read();
if (v & 1) swap(u,v);
if (!reach[u] && !reach[v]){
puts("-1");
return 0;
}
G.build(u,v,INF);
}
printf("%.1lf\n",G.maxflow());
return 0;
}