HDU 5045 Contest 费用流

题意:N个人要回答M道题。给出每个人回答每道题成功的概率。同时有限制:任何时刻,任意两个人回答问题的个数的差不能超过一个。求这些人回答完这些问题后,期望的回答正确的个数。

思路:因为任何时刻,任意两个人的回答问题的个数不能超过一个。这样其实是把这M个问题分组了。每一组有N个问题,在这N个问题中,怎样分配答题方案使期望的回答正确的个数最多。

这样就转化成了指派问题,可以用费用流求解。

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

const double EPS  = 1e-6;
const double INF = 0x3f3f3f3f;//无穷大值

int dcmp(double x)
{
    if(fabs(x) < EPS) return 0;
    else if(x > 0) return 1;
    else return -1;
}

struct edge
{
    int from, to, cap, flow;
    double cost;
    edge(int u = 0, int v = 0, int c = 0, int f = 0, double w = 0) :
        from(u), to(v), cap(c), flow(f), cost(w) {}
};
struct mcmf
{
    static const int MAX = 300;//点的数目
    edge edges[2 * MAX];//储存边的数组
    int head[MAX];//每个节点对应链表的开始位置
    int next[MAX];//链表的下一个节点在edges数组的位置
    int tot;//edges数组的大小
    int que[MAX], front, tail;//spfa用队列
    bool inq[MAX];//spfa入队标记
    double dis[MAX];//spfa距离
    int p[MAX];//增广路中,节点前面的一个弧的标号
    int a[MAX];//可行的增广流
    int src, sink;//src源点,sink汇点
    int flow;
    double cost;//求出的最大流,最小花费
    void init()
    {
        memset(head, -1, sizeof(head));
        tot = 0;
    }
    void addedge(int from, int to, int cap, double cost)
    {
        edges[tot] = edge(from, to, cap, 0, cost);
        next[tot] = head[from], head[from] = tot++;
        edges[tot] = edge(to, from, 0, 0, -cost);
        next[tot] = head[to], head[to] = tot++;
    }
    bool spfa(int & flow, double &cost)
    {
        fill(dis,dis+MAX,INF);
        memset(inq, false, sizeof(inq));
        dis[src] = 0.0, inq[src] = true, p[src] = 0, a[src] = INF;
        front = tail = 0;
        que[tail++] = src;
        while (front < tail)
        {
            int u = que[front++];
            inq[u] = false;
            for (int v = head[u]; v != -1; v = next[v])
            {
                edge & e = edges[v];
                if (e.cap > e.flow && dcmp(dis[e.to] - dis[u] - e.cost )> 0)
                {
                    dis[e.to] = dis[u] + e.cost;
                    p[e.to] = v;
                    a[e.to] = min(a[u], e.cap - e.flow);
                    if (!inq[e.to])
                        que[tail++] = e.to, inq[e.to] = true;
                }
            }
        }
        if (dcmp(dis[sink] -INF) == 0) return false;
        flow += a[sink];
        cost += dis[sink];//单位费用,整体费用
        for (int u = sink; u != src; u = edges[p[u]].from)
        {
            edges[p[u]].flow += a[sink];
            edges[p[u] ^ 1].flow -= a[sink];
        }
        return true;
    }
    double mincost(int s, int t)
    {
        src = s, sink = t;
        flow = 0;
        cost = 0;
        while (spfa(flow, cost));
        return cost;
    }
} solver;


int  t;
int N,M;
double map[15][1005];
double ans=0;
int main()
{
    //freopen("input.txt","r",stdin);
    scanf("%d",&t);
    int ca;
    for(ca=1; ca<=t; ca++)
    {
        scanf("%d %d",&N,&M);
        int i,j;
        ans=0;
        for(i=1; i<=N; i++)
            for(j=1; j<=M; j++)
            {
                scanf("%lf",&map[i][j]);
            }
        int num;
        int ii,jj;
        for(num=0; num<M/N; num++)
        {
            solver.init();
            for(ii=1; ii<=N; ii++)
            {
                solver.addedge(0,ii,1,0);
                for(jj=1+N; jj<=N+N; jj++)
                    solver.addedge(ii,jj,1,-map[ii][jj+num*N-N]);
            }
            for(jj=1+N; jj<=N+N; jj++)
                solver.addedge(jj,2*N+1,1,0);
            ans-=solver.mincost(0,2*N+1);
        }
        if(M%N!=0)
        {
            solver.init();
            for(ii=1; ii<=N; ii++)
            {
                solver.addedge(0,ii,1,0);
                for(jj=1+N; jj<=M%N+N; jj++)
                    solver.addedge(ii,jj,1,-map[ii][jj+M/N*N-N]);
            }
            for(jj=1+N; jj<=M%N+N; jj++)
                solver.addedge(jj,M%N+N+1,1,0);
            ans-=solver.mincost(0,M%N+N+1);
        }
        printf("Case #%d: %.5f\n",ca,ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值