【最小费最大流 && 概率论】HDU - 5988 Coding Contest

Problem Description

给你n个地点,m条边。接下来n个地点每个地点有num个人,food个人的食物。接下来m行,u地点到v地点,能经过的人数num,经过一个人发生故障的概率w, 第一个人通过的时候,发生故障概率为0。让你尽可能多的人吃到午餐,发生故障的最小概率

思路:

发生故障的概率 = 1 - 不发生故障的概率。每条边不发生故障的概率1-w。发生故障概率为0所以不发生故障概率为1。我们要求发生故障概率最小,那就是求不发生故障的概率最大。所以变成了求最大费
还有一点不能理解,反向费用的边为何是-w不是1/w。

#include<bits/stdc++.h>
using namespace std;
#define nn 105
#define inf 0x3f3f3f3f
struct node
{
    int u, to, c, next;
    double w;
};
node Map[500000];
int head[3*nn], cnt, vis[3*nn], pre[3*nn], flow[3*nn];
double dist[3*nn];
void add(int u, int v, int c, double w)
{
    Map[cnt].u = u;
    Map[cnt].to = v;
    Map[cnt].c = c;
    Map[cnt].w = w;
    Map[cnt].next = head[u];
    head[u] = cnt++;

    Map[cnt].u = v;
    Map[cnt].to = u;
    Map[cnt].c = 0;
    Map[cnt].w = -w;//为何是-w,不是1/w。-w AC, 1/w TLE.
    Map[cnt].next = head[v];
    head[v] = cnt++;
}
bool spfa(int s, int e)//在求最大费的过程,求出限制流量,不然会超时。
{
    memset(dist, 0, sizeof(dist));
    memset(vis, 0, sizeof(vis));
    memset(flow, 0, sizeof(flow));
    memset(pre, -1, sizeof(pre));
    queue<int> q;
    q.push(s);
    flow[s] = inf;
    dist[s] = 1;
    while(!q.empty())
    {
        s = q.front(); q.pop();
        vis[s] = 0;
        for(int i = head[s]; ~i; i = Map[i].next)
        {
            int to = Map[i].to, c = Map[i].c;
            double w = Map[i].w;
            if(c && dist[to] < dist[s] * w)
            {
                pre[to] = i;//记录路径
                dist[to] = dist[s] * w;
                flow[to] = min(flow[s], c);//求限制流量
                if(!vis[to])
                {
                    vis[to] = 1;
                    q.push(to);
                }
            }
        }
    }
    if(!dist[e]) return 0;
    else return 1;
}
double get_mincost(int s, int e)
{
    double ans = 1;
    while(spfa(s, e))
    {
        int p = pre[e];
        while(p != -1)
        {
            for(int i = 0; i < flow[e]; i++)
            {
                ans *= Map[p].w;
            }
            Map[p].c -= flow[e];
            Map[p^1].c += flow[e];
            p = pre[Map[p].u];
        }
    }
    return 1-ans;
}
int main()
{
    int T, u, v, num, n, m, food, i;
    double w;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &m);
        cnt = 0;
        memset(head, -1, sizeof(head));
        //建边:
        for(i = 1; i <= n; i++)
        {
            scanf("%d %d", &num, &food);
            add(0, i, num, 1);
            add(i+n, 2*n+1, food, 1);
            add(i, i+n, inf, 1);
        }
        while(m--)
        {
            scanf("%d %d %d %lf", &u, &v, &num, &w);
            if(num) {
                add(u, v, 1, 1);
                num--;
            }
            if(num) add(u, v, num, 1 - w);
        }
        printf("%.2f\n", get_mincost(0, 2*n+1));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值