题目链接: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. MySql按周,按月,按日分组统计数据

    知识关键词:DATE_FORMAT select DATE_FORMAT(create_time,'%Y%u') weeks,count(caseid) count from tc_case grou ...

  2. web html 在线调试工具

    html 的主要在线调试工具有以下几个: 1. plnkr.co 速度快,支持任意的外部css 和 js.  推荐. http://plnkr.co/edit/?p=preview 2. jsbin. ...

  3. 蓝牙发现服务UUID(service UUID)

    出至<蓝牙标准Core_V4.0>2.5.1 uuid(1576页) 其中 Bluetooth_Base_UUID定义为 00000000-0000-1000-8000-00805F9B3 ...

  4. 【Android】Eclipse快捷键精选

    1. ctrl+shift+r:打开资源 这可能是所有快捷键组合中最省时间的了. 这组快捷键可以让你打开你的工作区中任何一个文件,而你只需要按下文件名或mask名中的前几个字母,比如applic*.x ...

  5. 比Screen更好用的神器:tmux

    安装并启动 tmux tmux 应用程序的名称来源于终端(terminal)复用器(muxer)或多路复用器(multiplexer).换句话说,它可以将您的单终端会话分成多个会话. 它管理窗口和窗格 ...

  6. python 的正则表达式

    在python中,对正则表达式的支持是通过re模块来支持的.使用re的步骤是先把表达式字符串编译成pattern实例,然后在使用pattern去匹配文本获取结果. 其实也有另外一种方式,就是直接使用r ...

  7. idea生成类注释和方法注释的正确方法

    系统:Mac OS idea版本:2017.3.1 ---------------- 生成类注释 打开Preferences Editor -> File and Code Templates ...

  8. C# 内存理论与实践

    The C# Memory Model in Theory and Practice Best Practices All code you write should rely only on the ...

  9. How to get all Errors from ASP.Net MVC modelState?

    foreach (ModelState modelState in ViewData.ModelState.Values) { foreach (ModelError error in modelSt ...

  10. 0x800f0845 更新1803报错

    Windows 10累积更新KB4056892可能并不兼容AMD处理器,采用AMD Athlon 64 X2处理器的设备至少存在两起报告.