HDUOJ 5988 Coding Contest

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5988

题意:给你n个点,m条边,每个点有指定的人和食物的数量。每条边第一次经过不会触碰到电线,从第二次开始,每经过一次都有p的概率碰到,这条边最大允许通过人数是c,求如何让每个同学都取到食物而且碰到电线的概率最小。

思路:补集思维,求碰到电线概率最小即1-不碰到电线概率最大。不碰到电线的概率就是每条边上概率相乘,取个对数就可以转成加法了,然后考虑最小费用最大流建图:设立超级源点s和超级汇点t,如果该点人数大于食物数,那么源点向这个点连一条容量为人数和食物数之差,费用系数为0的边,否则这个点向汇点连一条人数和食物数之差,费用系数为0的边,然后对于给定的边按条件连就好了,注意要把容量为1的单独拉出来连边,因为第一次走过不会触碰到电线,然后跑下最小费用最大流,再把答案还原回来即可。有个坑是SPFA里面浮点数比较的时候要带上eps,不然会TLE

#pragma comment(linker, "/STACK:102400000,102400000")
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define lson root<<1,l,mid
#define rson root<<1|1,mid+1,r
#define Key_Value ch[ch[root][1]][0]
#define DBN1(a)           cerr<<#a<<"="<<(a)<<"\n"
#define DBN2(a,b)         cerr<<#a<<"="<<(a)<<", "<<#b<<"="<<(b)<<"\n"
#define DBN3(a,b,c)       cerr<<#a<<"="<<(a)<<", "<<#b<<"="<<(b)<<", "<<#c<<"="<<(c)<<"\n"
#define DBN4(a,b,c,d)     cerr<<#a<<"="<<(a)<<", "<<#b<<"="<<(b)<<", "<<#c<<"="<<(c)<<", "<<#d<<"="<<(d)<<"\n"
#define DBN5(a,b,c,d,e)   cerr<<#a<<"="<<(a)<<", "<<#b<<"="<<(b)<<", "<<#c<<"="<<(c)<<", "<<#d<<"="<<(d)<<", "<<#e<<"="<<(e)<<"\n"
#define DBN6(a,b,c,d,e,f) cerr<<#a<<"="<<(a)<<", "<<#b<<"="<<(b)<<", "<<#c<<"="<<(c)<<", "<<#d<<"="<<(d)<<", "<<#e<<"="<<(e)<<", "<<#f<<"="<<(f)<<"\n"
#define clr(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=10000+5;
const int INF=1e9;
const double eps=1e-8;
const int P=1000000007;
const double PI=acos(-1.0);
template<typename T>
inline T read(T&x){
    x=0;int _f=0;char ch=getchar();
    while(ch<'0'||ch>'9')_f|=(ch=='-'),ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x=_f?-x:x;
}
struct Edge{
    int from,to,cap,flow;
    double cost;
    Edge(){}
    Edge(int f,int t,int c,int fl,double co):from(f),to(t),cap(c),flow(fl),cost(co){}
};
struct MCMF{
    int n,m,s,t,k;
    vector<Edge> edges;
    vector<int> G[maxn];
    bool inq[maxn];
    double d[maxn];
    int p[maxn];
    int a[maxn];

    void init(int n,int s,int t){
        this->n=n, this->s=s, this->t=t;
        edges.clear();
        for(int i=0;i<=n;++i) G[i].clear();
    }

    void AddEdge(int from,int to,int cap,double cost){
        edges.push_back(Edge(from,to,cap,0,cost));
        edges.push_back(Edge(to,from,0,0,-cost));
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool SPFA(int &flow,double &cost){
        for(int i=0;i<n;++i) d[i]=INF;
        queue<int> Q;
        memset(inq,0,sizeof(inq));
        d[s]=0, Q.push(s), a[s]=INF, p[s]=0, inq[s]=true;
        while(!Q.empty()){
            int u=Q.front(); Q.pop();
            inq[u]=false;
            for(int i=0;i<G[u].size();++i){
                Edge &e=edges[G[u][i]];
                if(e.cap>e.flow && d[e.to]>d[u]+e.cost+eps){
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to]){ Q.push(e.to); inq[e.to]=true; }
                }
            }
        }
        if(d[t]==INF) return false;
        flow+=a[t];
        int u=t;
        while(u!=s){
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
            cost+=a[t]*edges[p[u]].cost;
            u=edges[p[u]].from;
        }
        return true;
    }

    double solve(){
        int flow=0;
        double cost=0;
        while(SPFA(flow,cost));
        return cost;
    }
}MM;
int T,n,m,s,b,u,v,f;
double p;
int main(){
    for(scanf("%d",&T);T--;){
        scanf("%d%d",&n,&m);
        MM.init(n+2,0,n+1);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&s,&b);
            int c=s-b;
            if(c>0) MM.AddEdge(0,i,c,0);
            else if(c<0) MM.AddEdge(i,n+1,-c,0);
        }
        for(int i=0;i<m;i++){
            scanf("%d%d%d%lf",&u,&v,&f,&p);
            p=-log(1.0-p);
            if(f>0) MM.AddEdge(u,v,1,0.0);
            if(f-1>0) MM.AddEdge(u,v,f-1,p);
        }
        double ans=MM.solve();
        ans=exp(-ans);
        printf("%.2f\n",1.0-ans);
    }
    return 0;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注