HDU 5988 Coding Contest 最小费用流变形

本文介绍了一道关于网络流的编程竞赛题,任务是在保证所有参赛者都能获取午餐的前提下,寻找使网络崩溃可能性最小的路径方案。文章通过将问题转化为费用流问题,并采用对数转换来解决浮点数运算问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

Problem Description
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 u_i-th block to the v_i-th block. Your task is to solve the lunch issue. According to the arrangement, there are s_i competitors in the i-th block. Limited to the size of table, b_i 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 p_i to touch
the wires and affect the whole networks. Moreover, to protect these wires, no more than c_i 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.

Input
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 b_i (s_i , b_i ≤ 200).
Each of the next M lines contains three integers u_i , v_i and c_i(c_i ≤ 100) and a float-point number p_i(0 < p_i < 1).
It is guaranteed that there is at least one way to let every competitor has lunch.

Output
For each turn of each case, output the minimum possibility that the networks would break down. Round it to 2 digits.

Sample Input

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

Sample Output

0.50

Source

2016ACM/ICPC亚洲区青岛站-重现赛(感谢中国石油大学)

题目分析:

其实早就应该写这个题目了。这是我唯一一次ICPC经历的能做却没做出来的题了。虽然最后因为罚时优势拿了铜,但是还不算太满意。可惜我网络流这部分后期就没有怎么练。照着模板也没敲出来。
题目其实还比较好理解。n个点,m条边,每个点有两个值,分别表示这个点需要出去的值和需要流进的值。
每条边有一个容量f和费用p。这个费用是一个概率,(0到1之间的浮点数),表示这条边崩溃的概率。
流经每条边第一次的值不会导致崩溃。
求这个系统崩溃的最小概率。
其实这个题只需要处理浮点数的问题就可以,将题中所设的乘法改成加法就是费用流模板题,只需要求对数即可。
每个点计算净流出或流入的值后分别和源汇点加边,在每条边特殊处理第一个流经的值(将其p值设为0),最后浮点数用eps来减少精度。
(其实现在看起来还是不难啊..)
(代码用kuangbin模板)

代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;
int n,m;

const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
const double eps=1e-8;
struct Edge
{
    int to,next,cap,flow;
    double cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN];
double dis[MAXN];
bool vis[MAXN];
int N;

void init(int n)
{
    N = n;
    tol = 0;
    memset(head,-1,sizeof(head));
}

void addedge(int u,int v,int cap,double cost)
{
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}

bool spfa(int s,int t)
{
    queue<int>q;
    for(int i = 0; i < N; i++)
    {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty())
    {

        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if(edge[i].cap > edge[i].flow &&
                    dis[v] > dis[u] + edge[i].cost + eps )
            {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1)return false;
    else return true;
}

int minCostMaxflow(int s,int t,double &cost)
{
    int flow = 0;
    cost = 0;
    while(spfa(s,t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            if(Min > edge[i].cap - edge[i].flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
        {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}

int a[MAXN],b[MAXN],c[MAXN];


int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init(n+50);
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            c[i]=a[i]-b[i];
        }
        int u,v,f;
        double p;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d%lf",&u,&v,&f,&p);
            p=-log10(1.0-p);
            if(f>0) addedge(u,v,1,0.0);
            if(f-1>0) addedge(u,v,f-1,p);
        }
        for(int i=1; i<=n; i++)
        {
            if(c[i]>0) addedge(0,i,c[i],0);
            else if(c[i]<0) addedge(i,n+1,-c[i],0);
        }
        double ans=0;
        minCostMaxflow(0,n+1,ans);
        ans=pow(10,-ans);
        printf("%.2f\n",1.0-ans);
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值