2016青岛区域赛.Coding Contest(费用流 + 概率计算转换为加法计算)

Coding Contest

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 5968    Accepted Submission(s): 1413

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 ui-th block to the vi-th block. Your task is to solve the lunch issue. According to the arrangement, there are sicompetitors in the i-th block. Limited to the size of table, bi 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 pi to touch
the wires and affect the whole networks. Moreover, to protect these wires, no more than ci 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 bi (si , bi ≤ 200).
Each of the next M lines contains three integers ui , vi and ci(ci ≤ 100) and a float-point number pi(0 < pi < 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
 
Recommend
jiangzijing2015
 
 
 /*************************************************************************
> File Name: coding_contest.cpp
> Author: CruelKing
> Mail: 2016586625@qq.com
> Created Time: 2019年09月18日 星期三 10时15分58秒
本题思路: 本题要求使得所有人都找到吃饭的地方且使得网络被破坏的可能性最小,因此
很简单可以看出来是个费用流,但是费用流一般都是统计增广路上的花费的和最小而这里我们
要求的概率为p1 * p2 * p.... * pn最小,因此我们可以想到,可以用对要算的式子取对数就很
容易可以将乘法转换为加法,即我们计算ln(p1) + ln(p2) + ... + ln(pn)然后对结果再取
e的指数(刚算的结果作为指数)很容易就可以求得正解,但是这里我们发现由于我们取得log
底数为e,且概率都是小于1的,因此我们得到费用的值都为负数,这样统计我们得到的是费用
的最大值也即概率的最大值就有错误,所以此时我们可以将问题转换为求解网络不被破坏的
可能行最大的问题,也就是使得(1 - p1) * (1 - p2) * ....* (1 - pn)最大,很容易我们
又可以将其转为log,得到ln(1 - p1) + ln(1 - p2) + ... + ln(1 - pn)最大,同样我们又
可以得到一堆负值,所以我们可以将问题转化非求解原问题的倒数最小的问题,那么很容易
我们就可以得到使得-(ln(1 - p1) + ln(1 - p2) + ... + ln(1 - pn))最小即可,也就是说
我们可以将边的花费设置为 - ln(1 - pi)然后跑费用流就可以了,具体如何建图呢?
我们知道每个结点初始情况可能下有人,而且每个结点还有一个上限,我们首先建立超级源点
s和超级汇点t,对于初始情况下的结点,如果一个结点的s[i] - b[i] > 0那么我们就从s到i
建一条容量为s[i] - b[i]的边,花费为0,如果s[i] - b[i] < 0我们就建一条从i到t的边,
容量为b[i] - s[i],花费为0,又由于第一次踩一根线网络不可能出问题,那我们就拆边,把
原来的边拆为一条容量为1花费为0,另一条容量为c[i] - 1花费为-log(1 - p[i])的边.
ok跑一波费用流.
************************************************************************/ #include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std; const int maxn = + , maxm = + , inf = 0x3f3f3f3f;
int n, m, s, t; const double eps = 1e-;
int head[maxn], tot; struct Edge {
int to, flow, cap, next;
double cost;
} edge[maxm << ]; void init() {
memset(head, -, sizeof head);
tot = ;
} void addedge(int u, int v, int cap, double cost) {
edge[tot].to = v; edge[tot].flow = ; edge[tot].cap = cap; edge[tot].cost = cost;
edge[tot].next = head[u]; head[u] = tot ++;
edge[tot].to = u; edge[tot].flow = ; edge[tot].cap = ; edge[tot].cost = -cost;
edge[tot].next = head[v]; head[v] = tot ++;
} int pre[maxn];
double dis[maxn];
bool vis[maxn];
int cnt[maxn]; bool spfa() {
queue<int> que;
for(int i = ; i <= n + ; i ++) {
dis[i] = inf;
vis[i] = false;
pre[i] = -;
cnt[i] = ;
}
cnt[s] = ;
dis[s] = 0.0;
vis[s] = true;
que.push(s);
while(!que.empty()) {
int u = que.front(); que.pop(); vis[u] = false;
for(int i = head[u]; ~i; 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]) {
cnt[v] ++;
vis[v] = true;
que.push(v);
if(cnt[v] > n) return false;
}
}
}
}
if(pre[t] == -) return false;
else return true;
} void mincostmxflow (double &ans) {
while(spfa()) {
int Min = inf;
for(int i = pre[t]; ~i; i = pre[edge[i ^ ].to]) {
if(Min > edge[i].cap - edge[i].flow) Min = edge[i].cap - edge[i].flow;
}
for(int i = pre[t]; ~i; i = pre[edge[i ^ ].to]) {
edge[i].flow += Min;
edge[i ^ ].flow -= Min;
ans += edge[i].cost * Min;
}
}
} int main() {
int _case, si, bi, u, v, c;
double pi;
scanf("%d", &_case);
while(_case --) {
init();
scanf("%d %d", &n, &m);
s = , t = n + ;
for(int i = ; i <= n; i ++) {
scanf("%d %d", &si, &bi);
if(si > bi) addedge(s, i, si - bi, 0.0);
else if(si < bi) addedge(i, t, bi - si, 0.0);
}
for(int i = ; i < m; i ++) {
scanf("%d %d %d %lf", &u, &v, &c, &pi);
if(c > ) addedge(u, v, , 0.0);
if(c > ) addedge(u, v, c - , -log2( - pi));
}
double ans = ;
mincostmxflow(ans);
printf("%.2f\n", 1.0 - pow(, -ans));
}
return ;
}
 
上一篇:【读书笔记《Android游戏编程之从零开始》】19.游戏开发基础(游戏音乐与音效)


下一篇:《C/C++学习指南》 - 关于本书