题目描述:
Problem Description
A coding contest will be held in this university, in a huge playground. The whole playground would be divided into N blocks, and there would be M directed paths linking these blocks. The i-th path goes from the u_i-th block to the v_i-th block. Your task is to solve the lunch issue. According to the arrangement, there are s_i competitors in the i-th block. Limited to the size of table, b_i bags of lunch including breads, sausages and milk would be put in the i-th block. As a result, some competitors need to move to another block to access lunch. However, the playground is temporary, as a result there would be so many wires on the path.
For the i-th path, the wires have been stabilized at first and the first competitor who walker through it would not break the wires. Since then, however, when a person go through the i - th path, there is a chance of p_i to touch
the wires and affect the whole networks. Moreover, to protect these wires, no more than c_i competitors are allowed to walk through the i-th path.
Now you need to find a way for all competitors to get their lunch, and minimize the possibility of network crashing.
Input
The first line of input contains an integer t which is the number of test cases. Then t test cases follow.
For each test case, the first line consists of two integers N (N ≤ 100) and M (M ≤ 5000). Each of the next N lines contains two integers si and b_i (s_i , b_i ≤ 200).
Each of the next M lines contains three integers u_i , v_i and c_i(c_i ≤ 100) and a float-point number p_i(0 < p_i < 1).
It is guaranteed that there is at least one way to let every competitor has lunch.
Output
For each turn of each case, output the minimum possibility that the networks would break down. Round it to 2 digits.
Sample Input
1
4 4
2 0
0 3
3 0
0 3
1 2 5 0.5
3 2 5 0.5
1 4 5 0.5
3 4 5 0.5
Sample Output
0.50
Source
2016ACM/ICPC亚洲区青岛站-重现赛(感谢中国石油大学)
题目分析:
其实早就应该写这个题目了。这是我唯一一次ICPC经历的能做却没做出来的题了。虽然最后因为罚时优势拿了铜,但是还不算太满意。可惜我网络流这部分后期就没有怎么练。照着模板也没敲出来。
题目其实还比较好理解。n个点,m条边,每个点有两个值,分别表示这个点需要出去的值和需要流进的值。
每条边有一个容量f和费用p。这个费用是一个概率,(0到1之间的浮点数),表示这条边崩溃的概率。
流经每条边第一次的值不会导致崩溃。
求这个系统崩溃的最小概率。
其实这个题只需要处理浮点数的问题就可以,将题中所设的乘法改成加法就是费用流模板题,只需要求对数即可。
每个点计算净流出或流入的值后分别和源汇点加边,在每条边特殊处理第一个流经的值(将其p值设为0),最后浮点数用eps来减少精度。
(其实现在看起来还是不难啊..)
(代码用kuangbin模板)
代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int n,m;
const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
const double eps=1e-8;
struct Edge
{
int to,next,cap,flow;
double cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN];
double dis[MAXN];
bool vis[MAXN];
int N;
void init(int n)
{
N = n;
tol = 0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,double cost)
{
edge[tol].to = v;
edge[tol].cap = cap;
edge[tol].cost = cost;
edge[tol].flow = 0;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u;
edge[tol].cap = 0;
edge[tol].cost = -cost;
edge[tol].flow = 0;
edge[tol].next = head[v];
head[v] = tol++;
}
bool spfa(int s,int t)
{
queue<int>q;
for(int i = 0; i < N; i++)
{
dis[i] = INF;
vis[i] = false;
pre[i] = -1;
}
dis[s] = 0;
vis[s] = true;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow &&
dis[v] > dis[u] + edge[i].cost + eps )
{
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
if(pre[t] == -1)return false;
else return true;
}
int minCostMaxflow(int s,int t,double &cost)
{
int flow = 0;
cost = 0;
while(spfa(s,t))
{
int Min = INF;
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
if(Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
}
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost * Min;
}
flow += Min;
}
return flow;
}
int a[MAXN],b[MAXN],c[MAXN];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init(n+50);
for(int i=1; i<=n; i++)
{
scanf("%d%d",&a[i],&b[i]);
c[i]=a[i]-b[i];
}
int u,v,f;
double p;
for(int i=0; i<m; i++)
{
scanf("%d%d%d%lf",&u,&v,&f,&p);
p=-log10(1.0-p);
if(f>0) addedge(u,v,1,0.0);
if(f-1>0) addedge(u,v,f-1,p);
}
for(int i=1; i<=n; i++)
{
if(c[i]>0) addedge(0,i,c[i],0);
else if(c[i]<0) addedge(i,n+1,-c[i],0);
}
double ans=0;
minCostMaxflow(0,n+1,ans);
ans=pow(10,-ans);
printf("%.2f\n",1.0-ans);
}
return 0;
}