题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5988
题意:n个点有人有面包,有一些边,除了第一个走这个边的人其他人都有pi的概率破坏网络,让你安排吃饭
方案,使得每个人吃上饭,并且破坏网络的概率最小。
解法:这是2016青岛赛区的银牌题,当时并不会做,今年学了一些网络流决心来A掉这个题。所以就有了下面
的的惨剧。
来说说做法吧,其实并不难。不过这里面相当于有一个拆边的思想。
破坏网络概率最小就是不破坏网络概率最大,那么就是一个乘积最大的表达式,加一个log就变成了加法,典
型的费用流问题。建边也很简单,源点连向人多余的,吃的多余的点连向汇点,中间建边记得拆边,因为第一
个人不要费用。。剩下的就是愉快的费用流一下。
坑点:double 的费用流如果你要写spfa的最短路里面有浮点数的比较大小,如果不加eps的修正会
tle。。。。
我T了10次就是没考虑到这个。
//HDU 5988
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-7;
const double inf = 2e9;
const int maxn = 205;
const int maxm = 50010;
struct node{
int st, en, next, flow;
double cost;
node(){}
node(int st, int en, int next, int flow, double cost):st(st),en(en),next(next),flow(flow),cost(cost){}
}E[maxm];
int p[maxn], num;
void init(){
num = 0;
memset(p, -1, sizeof(p));
}
void add(int st, int en, int flow, double cost){
E[num] = node(st,en,p[st],flow,cost); p[st] = num++;
E[num] = node(en,st,p[en],0,-cost); p[en] = num++;
}
int pre[maxn];
double dis[maxn];
bool fg[maxn];
bool spfa(int st, int en){
for(int i=0; i<=en; i++) fg[i]=0, dis[i]=inf, pre[i]=-1;
queue <int> q;
q.push(st);
fg[st]=1;
dis[st]=0;
while(!q.empty()){
int u = q.front(); q.pop();
fg[u]=0;
for(int i=p[u]; i+1; i=E[i].next){
int v=E[i].en;
if(E[i].flow&&dis[v]-dis[u]-E[i].cost>eps){
dis[v]=dis[u]+E[i].cost;
pre[v]=i;
if(!fg[v]){
fg[v]=1;
q.push(v);
}
}
}
}
if(pre[en]==-1) return 0;
else return 1;
// if(dis[en]+eps<inf) return 1;
// else return 0;
}
double solve(int st, int en)
{
double ans=0;
while(spfa(st, en)){
int d = 0x3f3f3f3f;
for(int i=pre[en]; i+1; i=pre[E[i].st]) d = min(d, E[i].flow);
for(int i=pre[en]; i+1; i=pre[E[i].st]){
E[i].flow-=d;
E[i^1].flow+=d;
ans += 1.0*d*E[i].cost;
}
}
return ans;
}
int main()
{
int t; scanf("%d", &t);
while(t--){
int n, m;
scanf("%d%d", &n, &m);
init();
for(int i=1; i<=n; i++){
int s, b;
scanf("%d%d", &s, &b);
int f = s-b;
if(f>0) add(0, i, f, 0);
else if(f<0) add(i, n+1, -f, 0);
}
while(m--){
int u,v,f;
double w;
scanf("%d%d%d%lf", &u,&v,&f,&w);
w = -log2(1.0-w);
if(f>0) add(u,v,1,0.0);
if(f-1>0) add(u,v,f-1,w);
}
double ans = solve(0,n+1);
ans = 1.0-pow(2.0,-ans);
printf("%.2f\n", ans);
}
return 0;
}