HDU5988/nowcoder 207G - Coding Contest - [最小费用最大流]
题目链接: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 - [最小费用最大流]的更多相关文章
- HDU 5988.Coding Contest 最小费用最大流
Coding Contest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)To ...
- HDU 5988 Coding Contest(最小费用最大流变形)
Problem DescriptionA coding contest will be held in this university, in a huge playground. The whole ...
- nowcoder 206A - Birthday - [最小费用最大流]
题目链接:https://www.nowcoder.com/acm/contest/206/A 题目描述 恬恬的生日临近了.宇扬给她准备了一个蛋糕.正如往常一样,宇扬在蛋糕上插了n支蜡烛,并把蛋糕分为 ...
- HDU–5988-Coding Contest(最小费用最大流变形)
Coding Contest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)To ...
- HDU - 6437 Problem L.Videos 2018 Multi-University Training Contest 10 (最小费用最大流)
题意:M个影片,其属性有开始时间S,结束时间T,类型op和权值val.有K个人,每个人可以看若干个时间不相交的影片,其获得的收益是这个影片的权值val,但如果观看的影片相邻为相同的属性,那么收益要减少 ...
- 2018牛客网暑期ACM多校训练营(第五场) E - room - [最小费用最大流模板题]
题目链接:https://www.nowcoder.com/acm/contest/143/E 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K ...
- HDU3376 最小费用最大流 模板2
Matrix Again Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others)To ...
- hdu 2686&&hdu 3376(拆点+构图+最小费用最大流)
Matrix Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- hdu 1853 Cyclic Tour (二分匹配KM最小权值 或 最小费用最大流)
Cyclic Tour Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/65535 K (Java/Others)Total ...
随机推荐
- MySql按周,按月,按日分组统计数据
知识关键词:DATE_FORMAT select DATE_FORMAT(create_time,'%Y%u') weeks,count(caseid) count from tc_case grou ...
- web html 在线调试工具
html 的主要在线调试工具有以下几个: 1. plnkr.co 速度快,支持任意的外部css 和 js. 推荐. http://plnkr.co/edit/?p=preview 2. jsbin. ...
- 蓝牙发现服务UUID(service UUID)
出至<蓝牙标准Core_V4.0>2.5.1 uuid(1576页) 其中 Bluetooth_Base_UUID定义为 00000000-0000-1000-8000-00805F9B3 ...
- 【Android】Eclipse快捷键精选
1. ctrl+shift+r:打开资源 这可能是所有快捷键组合中最省时间的了. 这组快捷键可以让你打开你的工作区中任何一个文件,而你只需要按下文件名或mask名中的前几个字母,比如applic*.x ...
- 比Screen更好用的神器:tmux
安装并启动 tmux tmux 应用程序的名称来源于终端(terminal)复用器(muxer)或多路复用器(multiplexer).换句话说,它可以将您的单终端会话分成多个会话. 它管理窗口和窗格 ...
- python 的正则表达式
在python中,对正则表达式的支持是通过re模块来支持的.使用re的步骤是先把表达式字符串编译成pattern实例,然后在使用pattern去匹配文本获取结果. 其实也有另外一种方式,就是直接使用r ...
- idea生成类注释和方法注释的正确方法
系统:Mac OS idea版本:2017.3.1 ---------------- 生成类注释 打开Preferences Editor -> File and Code Templates ...
- C# 内存理论与实践
The C# Memory Model in Theory and Practice Best Practices All code you write should rely only on the ...
- How to get all Errors from ASP.Net MVC modelState?
foreach (ModelState modelState in ViewData.ModelState.Values) { foreach (ModelError error in modelSt ...
- 0x800f0845 更新1803报错
Windows 10累积更新KB4056892可能并不兼容AMD处理器,采用AMD Athlon 64 X2处理器的设备至少存在两起报告.