题目链接:https://www.nowcoder.com/acm/contest/207/G

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5988

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
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 si competitors 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.
输入描述:
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.
输出描述:
For each turn of each case, output the minimum possibility that the networks would break down. Round it to 2 digits.
示例1
输入
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
输出
0.50

题意:

题目的灵感估计来源于现场赛发餐包的操作……

现在赛场划分成了 $N$ 个不相交区域,共有 $M$ 条有向路连接两个区域,

对于每个区域,给出 $s[i],b[i]$ 代表区域内有 $s[i]$ 个人,$b[i]$ 个餐包,一旦某人在本区域内拿不到餐包,就会前往其他区域获取餐包,

而众所周知,赛场上的路上是有很多电线的,一不小心就会踢到电线,所以现在每条路上都存在这一些电线,

现在已知,一旦某个选手走过第 $i$ 条有向边,就有 $p[i]$ 的概率踢到电线,进而影响整个电网,不过经过该路径的第一个人是必然不会踢到电线的,同时对于第 $i$ 条边,限制最多 $c[i]$ 个人走过。

现在,求整个电网被影响的最小概率。

题解:

首先,我们考虑既然将来还要深入学习数学相关知识,概率和期望这一块是怎么样都跑不掉的,所以还不如现在趁机好好巩固一下概率论的基础知识……

考虑每个人踢到电线的概率是相互独立的,我们将某次某个人经过某条边称作一次实验,

若每次实验踢到电线事件发生的概率相同,则 $n$ 个人踢到电线 $k$ 次的概率服从二项分布,众所周知二项分布的公式为

$P\left( {X = k} \right) = C_n^k p^k \left( {1 - p} \right)^{n - k}$

其中 $p$ 即为一次实验中发生踢到电线事件的概率;

而发生踢到电线这一事件发生 $1,2,3,\cdots$ 次均会影响电网,所以总共进行 $n$ 次独立实验后,电网被影响的概率为

$\sum\limits_{k = 1}^n {P\left( {X = k} \right)} = 1 - P\left( {X = 0} \right)$

易知

$P\left( {X = 0} \right) = \left( {1 - p} \right)^n$

但是我们知道,电网被影响的概率为

$P\left( {X = 1,2, \cdots ,n} \right) = 1 - \left( {1 - p} \right)^n$

当然,本题中,每次实验踢电线事件发生概率不一定相同,但是概率的计算方法依然符合上式:

$P = 1 - \prod\limits_{i = 1}^n {\left( {1 - p\left[ i \right]} \right)}$

然后需要考虑如何进行计算最小概率,显然本题为费用流题,那么不妨将选手的移动看做流的流动,故:

1、若第 $i$ 个区域内,选手比餐包多($s[i]>b[i]$),显然选手要流出,因此从源点向每个这样的区域连边,流量上限为 $s[i] - b[i]$(这部分人要流出),费用为 $0$;

2、若第 $i$ 个区域内,选手比餐包少($s[i]<b[i]$),显然选手要流入,因此从每个这样的区域向汇点连边,流量上限为 $b[i] - s[i]$(要流入这么多人),费用为 $0$;

3、若第 $i$ 个区域内,选手和餐包一样多($s[i]=b[i]$),这样的区域不用动了,选手老老实实待在自己区域内吃饭就行。

而题目中给出的 $M$ 条有向边,正好是可供选手流动的边,因此对于 $M$ 条有向边中任意一条 $edge(u,v,c,p)$:建立从节点 $u$ 连向节点 $v$ 的有向边,显然流量上限为 $c$,

而剩下来的费用是比较难搞的,首先,暂时不考虑第一个人经过某条路必然不会踢到电线这件事情,则因为每 $1$ 单位的流量流过该条边,正好就相当于一次实验,

那么就要贡献 $\times \left( {1 - p\left[ i \right]} \right)$,而 $k$ 个单位的流量流过该条边的贡献即为 $\times \left( {1 - p\left[ i \right]} \right)^k$,不难想到进行取对数操作,就有

$\log \left[ {\left( {1 - p\left[ 1 \right]} \right)^{k_1 } \times \left( {1 - p\left[ 2 \right]} \right)^{k_2 } \times \cdots \times \left( {1 - p\left[ n \right]} \right)^{k_n } } \right] = k_1 \log \left( {1 - p\left[ 1 \right]} \right) + k_2 \log \left( {1 - p\left[ 2 \right]} \right) + \cdots + k_n \log \left( {1 - p\left[ n \right]} \right)$

这样一来,就可以变成可以套用费用流的形式了,由于考虑到费用都是正数,将 $\log \left( {1 - p\left[ i \right]} \right)$ 取负,作为每条边的费用值,这样一来,求出来的费用值就是

$k_1 \left[ { - \log \left( {1 - p\left[ 1 \right]} \right)} \right] + k_2 \left[ { - \log \left( {1 - p\left[ 2 \right]} \right)} \right] + \cdots + k_n \left[ { - \log \left( {1 - p\left[ n \right]} \right)} \right]$

那么,要 $P = 1 - \prod\limits_{i = 1}^n {\left( {1 - p\left[ i \right]} \right)}$ 越小越好,即要 $\log \left[ {\left( {1 - p\left[ 1 \right]} \right)^{k_1 } \times \left( {1 - p\left[ 2 \right]} \right)^{k_2 } \times \cdots \times \left( {1 - p\left[ n \right]} \right)^{k_n } } \right]$ 越大越好(只要底数大于 $1$),也即 $k_1 \log \left( {1 - p\left[ 1 \right]} \right) + k_2 \log \left( {1 - p\left[ 2 \right]} \right) + \cdots + k_n \log \left( {1 - p\left[ n \right]} \right)$ 越大越好,也即 $k_1 \left[ { - \log \left( {1 - p\left[ 1 \right]} \right)} \right] + k_2 \left[ { - \log \left( {1 - p\left[ 2 \right]} \right)} \right] + \cdots + k_n \left[ { - \log \left( {1 - p\left[ n \right]} \right)} \right]$ 越小越好。

然后,在考虑第一个人经过某条路必然不会踢到电线这件事情,我刚开始考虑是对MCMF板子动点手脚,每次跑出来的 cost 减去那些第一次走人的边,事实和理论均证明这样是行不通的,

而费用流的题,一般都是在见图上搞文章,改板子改多了容易出事情……所以,实际上,我们把上面那些 $edge(u,v,c,p)$ 拆成两条边即可,

第一条 $edge(u,v,1,0)$,第二条 $edge(u,v,c-1,- \log(1-p))$,这样一来,最小费用的性质就可以保证第一条边空着必然先跑第一条,然后才考虑第二条。

所以,网络流,最精妙的,就是建图,少想点改板子的操作,多想点建图的操作,实在不行了再改板子(改最好也不要魔改……很容易就炸了……显然不影响板子正确性的前提下小改即可)。

所以,整道题就是建图后最小费用最大流,求出费用后,再取负再pow回去即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=;
const int INF=0x3f3f3f3f;
const double eps=1e-; struct Edge{
int u,v,cap,flow;
double cost;
};
struct MCMF
{
int s,t; //源点汇点
vector<Edge> E;
vector<int> G[maxn];
void init(int l,int r)
{
E.clear();
for(int i=l;i<=r;i++) G[i].clear();
}
void addedge(int from,int to,int cap,double cost)
{
E.push_back((Edge){from,to,cap,,cost});
E.push_back((Edge){to,from,,,-cost});
G[from].push_back(E.size()-);
G[to].push_back(E.size()-);
} double d[maxn];
int vis[maxn];
int aug[maxn],pre[maxn];
bool spfa(int s,int t,int &flow,double &cost)
{
for(int i=s;i<=t;i++) d[i]=INF;
memset(vis,,sizeof(vis));
queue<int> q;
q.push(s);
d[s]=, vis[s]=, pre[s]=, aug[s]=INF;
while(!q.empty())
{
int now=q.front(); q.pop();
vis[now]=;
for(int i=;i<G[now].size();i++)
{
Edge& e=E[G[now][i]]; int nxt=e.v;
if(e.cap>e.flow && d[nxt]>d[now]+e.cost+eps)
{
d[nxt]=d[now]+e.cost;
pre[nxt]=G[now][i];
aug[nxt]=min(aug[now],e.cap-e.flow);
if(!vis[nxt])
{
q.push(nxt);
vis[nxt]=;
}
}
}
}
if(d[t]==INF) return ;
flow+=aug[t];
cost+=d[t]*aug[t];
for(int i=t;i!=s;i=E[pre[i]].u)
{
E[pre[i]].flow+=aug[t];
E[pre[i]^].flow-=aug[t];
}
return ;
} double mincost()
{
int flow=;
double cost=;
while(spfa(s,t,flow,cost));
return cost;
}
}mcmf; int n,m;
int s[maxn],b[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
mcmf.init(,n+);
mcmf.s=;
mcmf.t=n+;
for(int i=;i<=n;i++)
{
scanf("%d%d",&s[i],&b[i]);
if(s[i]>b[i]) mcmf.addedge(mcmf.s,i,s[i]-b[i],0.0);
if(s[i]<b[i]) mcmf.addedge(i,mcmf.t,b[i]-s[i],0.0);
}
for(int i=;i<=m;i++)
{
int u,v,c; double p;
scanf("%d%d%d%lf",&u,&v,&c,&p);
if(c<=) continue;
mcmf.addedge(u,v,c-,-log2(-p));
mcmf.addedge(u,v,,0.0);
}
printf("%.2f\n",-pow(,-mcmf.mincost()));
}
}

补充一些注意的点:

首先,要考虑浮点误差,1.00000000000003 > 1.00000000000002 类似这种情况,很有可能是浮点误差造成的,而非真的前者大于后者,所以我们要设定一个精度,不影响到解题的正确性,又可以避免浮点误差。

其次,考虑取对数时的底数尽量小,这题之前有人说卡long double,其实大概是因为底数取大了,取成了 $10$,这样一来边的费用就会变小,double的精度就可能不够了。

HDU5988/nowcoder 207G - Coding Contest - [最小费用最大流]的更多相关文章

  1. HDU 5988.Coding Contest 最小费用最大流

    Coding Contest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)To ...

  2. HDU 5988 Coding Contest(最小费用最大流变形)

    Problem DescriptionA coding contest will be held in this university, in a huge playground. The whole ...

  3. nowcoder 206A - Birthday - [最小费用最大流]

    题目链接:https://www.nowcoder.com/acm/contest/206/A 题目描述 恬恬的生日临近了.宇扬给她准备了一个蛋糕.正如往常一样,宇扬在蛋糕上插了n支蜡烛,并把蛋糕分为 ...

  4. HDU–5988-Coding Contest(最小费用最大流变形)

    Coding Contest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)To ...

  5. HDU - 6437 Problem L.Videos 2018 Multi-University Training Contest 10 (最小费用最大流)

    题意:M个影片,其属性有开始时间S,结束时间T,类型op和权值val.有K个人,每个人可以看若干个时间不相交的影片,其获得的收益是这个影片的权值val,但如果观看的影片相邻为相同的属性,那么收益要减少 ...

  6. 2018牛客网暑期ACM多校训练营(第五场) E - room - [最小费用最大流模板题]

    题目链接:https://www.nowcoder.com/acm/contest/143/E 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K ...

  7. HDU3376 最小费用最大流 模板2

    Matrix Again Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)To ...

  8. hdu 2686&&hdu 3376(拆点+构图+最小费用最大流)

    Matrix Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Subm ...

  9. hdu 1853 Cyclic Tour (二分匹配KM最小权值 或 最小费用最大流)

    Cyclic Tour Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/65535 K (Java/Others)Total ...

随机推荐

  1. 让 Python 更加充分的使用 Sqlite3

    我最近在涉及大量数据处理的项目中频繁使用 sqlite3.我最初的尝试根本不涉及任何数据库,所有的数据都将保存在内存中,包括字典查找.迭代和条件等查询.这很好,但可以放入内存的只有那么多,并且将数据从 ...

  2. [ci] jenkins kubernetes插件配置(容器模式)-通过jnlp

    有个小伙用sh结合jenkins搞的k8s cicd还不错 jenkins kubernetes插件 首先插件管理,搜索kubernetes plugin安装 配置kubernetes云 配置项目 执 ...

  3. [Big Data - Kafka] Kafka设计解析(四):Kafka Consumer解析

    High Level Consumer 很多时候,客户程序只是希望从Kafka读取数据,不太关心消息offset的处理.同时也希望提供一些语义,例如同一条消息只被某一个Consumer消费(单播)或被 ...

  4. emacs自动折行设置

    - emacs自动折行     - 临时设置下 M-x `toggle-truncate-lines`    - init.el 中添加 `(toggle-truncate-lines 1)`

  5. python 中的i++ ,逻辑表达式

    1.关于i++ python 中的没有 i++ ,如果写了会报语法错误. 但是python 中有 --i,++i,+-i,-+i,他们不是实现-1操作的,仅仅是作为判断运算符号,类似数学中的负负得正 ...

  6. 15款css3鼠标悬停图片动画过渡特效

    分享15款css3鼠标悬停图片动画过渡特效.这是一款15款不同效果的css3 hover动画过渡效果代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class ...

  7. 解决SQLite异常:library routine called out of sequence

    在项目开发中,使用SQLite一不小心就会碰到各种DB异常,网上搜了下没有这方面的资料,写出来记录下. 异常信息:android.database.sqlite.SQLiteMisuseExcepti ...

  8. idea配置项目运行时内存大小

    选择 edit Configurations : -server -XX:PermSize=1024M -XX:MaxPermSize=2048M

  9. 关于ECMP 等价路由

    1.ECMP简介 Equal-CostMultipathRouting,等价多路径.即存在多条到达同一个目的地址的相同开销的路径.当设备支持等价路由时,发往该目的 IP 或者目的网段的三层转发流量就可 ...

  10. Cocos2dx网络读取图片

    // // Connection.h // XXDemo // // Created by LeeHonGee on 14-9-4. // // #ifndef __XXDemo__Connectio ...