hdu 5988 Coding Contest (费用流变形)

来源:互联网 发布:中国农大远程网络教育 编辑:程序博客网 时间:2024/06/05 20:52

题意:给定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就好了。

------------------------------------------***********************************-----------------------------

然而我的模板很是垃圾,一个点到另一个点不能有多条边,然后GG,所以找了别人能过得一份代码当作我的模板

#include<cstdio>#include<string.h>#include<queue>#include<algorithm>#define maxm 50000#define inf 1e8using 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,饭Bvoid 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;}



原创粉丝点击