Coding Contest
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1035 Accepted Submission(s): 189
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.
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.
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个人,有B盒饭。每条边只能被走c次,每条边上都有电线,第一个人通过的时候,不会破坏电线,从第二个人开始,每次都有概率p破坏掉电线。使得每个人都能吃饭,求最小破坏电线的概率。
解法:每条边有走的次数(流量),每条边走一次发生破坏概率为p(流量1,费用p),容易想到费用流。可是费用流往往是费用相加的,这个是概率,只能相乘。有什么办法,log函数可以把乘除法转换为加减法。所以对每个概率取个log当成费用就行了。
log取底数取个2,然后对每条边的概率值取个对数,跑一次最小费用流,感觉没什么问题,但是会wa,因为概率总是小于1的,而底数是2,这样取log后会变为负数。费用为负,跑出来的费用就会朝更小走,在这个题上会出问题。那么取个负呢,把负变成正,还是会出问题,取负之后最小就变成了最大,跑出来是最大费用,也是会出问题的。
这时候就应该从反方向进行考虑,求踩坏的最小概率,就是求不踩坏的最大概率,1-p后取log,和以上同理,求出了最大费用。取出来还回去后用1减一下就好了。
新建源点s,汇点t,对于S>B的需要人走,从源点连一条流量为S[i]-B[i],费用为0(出门不需要费用)的边过去,add(s,i,S[i]-B[i],0),对于s<b的,add(i,t,B[i]-S[i],0)。
然后还有一个问题,就是第一次踩的时候,不会触发,那么从原有的边中取一条出来,流量1,费用0就好了。
还有一点是,我这个代码是玄学AC,最快一次795MS,但是也T了几次,不是很懂为什么。
这个做法是赛后火山学长给我点了一下,然后我自己YY一下做出来的,没看过网上的题解,也不知道有没有什么更好的做法,这道题的目标只是自己能做出来就行了,也不想去管更好的做法,毕竟管不了了。
///2016.11.13
#include<cstdio>
#include<string.h>
#include<queue>
#include<algorithm>
#define maxm 50000
#define inf 1e8
using namespace std;
const int maxn = 200;
const double eps = 1e-7;
const float EXP = exp(1.0);
int num, p[maxn]; ///邻接表头结点
struct EDGE
{
int u, v, flow, next;
double cost;
EDGE() {}
EDGE(int u, int v, int flow,double cost, int next): u(u), v(v), flow(flow),cost(cost), next(next) {}
} E[maxm];
int n,m;
int S[maxn],B[maxn]; ///每个点的人数S,饭B
void init() ///初始化
{
num = 0;
memset(p, -1, sizeof p);
}
void add(int u, int v, int flow, double cost)
{
E[num] = EDGE(u, v, flow, cost, p[u]);
p[u] = num++;
E[num] = EDGE(v, u, 0, -cost, p[v]);
p[v] = num++;
}
int pre[maxn], mi[maxn];
double dis[maxn];
bool inq[maxn];
int s, t; ///源点汇点
double ans;
int flow;
int que[maxn];
bool spfa() ///火山哥给的模版,挺快的,我只会用
{
for(int i = 0; i <=t; i++)
inq[i] = 0, dis[i] = inf,pre[i]=-1;
dis[s] = 0, mi[s] = inf, inq[s] = 1;
int l = 0, r = 1;
que[l] = s;
while(l != r)
{
int u = que[l++];
if(l == maxn) l =0;
inq[u] = 0;
for(int i = p[u]; i + 1; i = E[i].next)
{
int v = E[i].v;
if(E[i].flow && dis[v] > dis[u] + E[i].cost+eps)
{
dis[v] = dis[u] + E[i].cost;
pre[v] = i;
mi[v] = min(mi[u], E[i].flow);
if(!inq[v])
{
inq[v] = 1;
que[r++] = v;
if(r >= maxn) r -= maxn;
}
}
}
}
if(pre[t]==-1) return false;
flow += mi[t];
ans += mi[t] * dis[t];
int u = t;
for(int i = pre[u]; i + 1; i = pre[E[i].u])
{
E[i].flow -= mi[t];
E[i ^ 1].flow += mi[t];
}
return true;
}
double Mincost()
{
ans = 0, flow = 0;
while(spfa());
return ans;
}
int main(void)
{
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&n,&m);
s = 105,t = 106;
for(int i = 1;i <= n;i++){
scanf("%d%d",&S[i],&B[i]);
if(S[i] > B[i]){ ///人数大于饭数,源点建边出来
add(s,i,S[i]-B[i],0);
}
if(B[i] > S[i]){ ///人数小于饭数,建边到汇点
add(i,t,B[i]-S[i],0);
}
}
for(int i = 1;i <= m;i++){
int u,v,f;
double pp;
scanf("%d%d%d%lf",&u,&v,&f,&pp);
add(u,v,1,0); ///第一次走没有费用
add(u,v,f-1,-log2(1-pp)); ///流量-1
}
double tmp = Mincost();
printf("%.2f\n",1-pow(2,-tmp));
}
return 0;
}
想想这估计是我在hdu做的最后一道题,也是我ACM生涯的最后一道题以及最后一道题解了。青岛现场赛没过这道题,当时根本想到拆边的做法。没想到网络流会是我去的现场赛的银牌题。其实感觉这道题也没这么难想,只怪我网络流那些模型的题没刷得完,使得网络流这些的基本模型都没掌握完,遇到变形就真的手足无措。想辩解一下,又觉得没必要,弱就是弱,不需要解释。对ACM我确实没有那种热爱,即使坐下来认真去做题,也不能总是全神贯注,像做喜欢的事那样去思考和专研。AC确实可以带来非常爽的感觉,但是在这个过程中,常常会做一道题做到扣脑壳也找不出BUG,然后在网上去找ac代码进行对拍,这样ac后带来的快感就降低了很多。总之就是过程当中得到的与失去的不成正比,觉得不亏,但是也不赚。但是我继续做下去,我觉得以我的态度,肯定是不能做好的。
还有后来才意识到的一个问题,就是我发现我大二做过的一些专题训练的题,很多属于银牌题,金牌题,但是那个时候我铜牌水平都没达到,我去那种题感觉是收效甚微的,除了会套个模版什么也不会。我觉得正常的过程应该是对基础代码能力有一个量的训练,因为基础代码能力属于铜牌题,也是银牌和金牌题的功底。没有基础代码能力,后面的都是空谈。也就是基本的各种思维能力都没有,还说个鸡霸算法。然而我跨过了这个过程,去学习一些新算法,我觉得这样是收效甚微的,我觉得我浪费的时间简直不是一点半点。难以想象为什么一个cf1500多分的人会去死命磕割点和桥边双联通点双联通强联通网络流这些。不应该先把刷各种基础题,把分提到1700以上再去考虑学习新算法吗。或者一边学习新算法,一边做大量代码能力的题,在水平不够的情况下,我觉得基础代码能力才是关键。哎,可是我一直没意识到这个问题,意识到的时候已经大三了。后面想努力补回来,我觉得是不行的,因为智商不够,数学不好,又不够勤奋(不然就不会来这个大学了)。不过这届大二的已经意识到了这个问题,哎,我们当年嗯是什么机会都没有,羡慕这届大二的。
虽然只去过一次区域赛,而且我很弱,我很菜,但是加入这个实验室,学到的东西还是很多的。最重要的一点就是见识了世界有多大,虽然没去过更大的舞台,但是已经深深感受到了。比赛的时候,旁边全都是各种985、211,在场的大部分人以后都会成为程序员,我也会程序员(没有其他选择了)。在场的很多也是大学才开始学的,种种表现能看得出他们很多人比我优秀很多很多,我不知道我以后有什么能力可以和他们去竞争,或许根本无法同台,我只是个搬砖的。还有清华clj,杜教,鏼爷就在旁边的旁边,这辈子有幸离大神这么近,本来想去要个签名,总觉得有点不合适,作为一个粉丝去要签名,我觉得是没什么的,但是在赛场上,作为对手(虽然根本没资格,不过站在那里了,我就是这么想的),要签名就有点看不过去。还见识了一件事情,就是偶像的力量。先把自己比作屌丝,某j比作我的偶像,然后看到一个非常漂亮的妹纸,屌丝不好意思与之接近,妹纸偶像某j,去要签名要合照。得到一个体会,偶像轻而易举得到的是屌丝梦寐以求却得不到的。
通过这个实验室,我觉得我视野边开阔了,我总喜欢自嘲自己是辣鸡,旁边人又说你是辣鸡我们是什么。虽然我自嘲辣鸡,但我确实就是一个辣鸡,因为我见识了很大的舞台,知道自己的渺小,不能妄自菲薄。我下三楼去软件实验室了,在今后的过程中,我觉得我不会因为做出一个小的APP,一个网页而觉得我自己多厉害,因为我知道这只是一个很不起眼事情,虽然旁边人可能会觉得我很厉害,但是我知道的是,我以后出去肯定不是和这些人竞争,在他们面前的优越感并没有什么意义。我知道学校以外厉害的人简直数都数不清,他们才是我的目标。加油,我是最胖的!