hdu 4406

最大费用流,就是流量不固定的最大费用流。不一定是最大流。

就是经典的费用与流量平方成正比的最小流(白书上有)模型的应用。

一个流,代表一个分配。如果所有科目都及格的话,直接拆边就可以了。

这里要考虑的就是如果科目不及格的情况。考虑如何加边能够使最大费用流是保证所有的不及格的科目都及格这一条件下的最大费用流,向s和表示各个不及格科目的节点之间连一条费用足够大(这个”大“大于所有科目(不及格的定为60)都加到100所增加的pi*wi的和)的边,流量为60-科目的分数。如果有能够使所有不及格的科目及格的方案,那么求了最大流之后的分配方案肯定使所有的不及格的科目及格了(因为之前的那个足够大的费用的设置,使任何一种没有使所有的不及格科目及格的可行流的总费用小于使其及格的费用),也就可间接的求出保证所有科目及格的条件下,可得到的最大费用流(即所能增加的pi*wi的最大值)。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <string>
#include <set>
#define LL long long
#define myabs(x) ((x)>0?(x):(-(x)))
using namespace std;
const int inf=0x3f3f3f3f;
const double tinf=100000;
const int maxn=100;
const int maxm=1500*2;
struct Edge
{
    int from,to,next;
    double val,cap,flow;
};
Edge e[maxm];
int head[maxn];
int cre[maxn],bas[maxn],vis[maxn];
double dis[maxn],score[110];
int p[maxn];
int n,kin,m,tot,s,t;
void addedge(int from,int to,double cap,double val)
{
    e[tot].from=from; e[tot].to=to;
    e[tot].cap=cap; e[tot].flow=0;
    e[tot].val=val;
    e[tot].next=head[from];
    head[from]=tot++;
    e[tot].from=to; e[tot].to=from;
    e[tot].cap=0; e[tot].flow=0;
    e[tot].next=head[to];
    e[tot].val=-val;
    head[to]=tot++;
}
double mfmc()
{
    queue<int> q;
    int f;
    double sum=0;
    int i;
    for(;;)
    {
        for(i=s;i<=t;i++) dis[i]=-inf;
        memset(vis,0,sizeof(vis));
        dis[s]=0;
        vis[s]=1;
        q.push(s);
        int i,v;
        while(!q.empty())
        {
            f=q.front(); q.pop();
            vis[f]=0;
            for(i=head[f];i!=-1;i=e[i].next)
            {
                v=e[i].to;
                if(e[i].cap>e[i].flow&&dis[f]+e[i].val>dis[v])
                {
                    p[v]=i;
                    dis[v]=dis[f]+e[i].val;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        if(dis[t]<=0) break;
        double a=(double)inf;
        for(i=t;i!=s;)
        {
            a=min(a,e[p[i]].cap-e[p[i]].flow);
            i=e[p[i]].from;
        }
        sum+=a*dis[t];
        for(i=t;i!=s;)
        {
            e[p[i]].flow+=a;
            e[p[i]^1].flow-=a;
            i=e[p[i]].from;
        }
    }
    return sum;
}
int main()
{
    int i;
    for(i=60;i<=100;i++) score[i]=4.0-3.0*(100-i)*(100-i)/1600;
    while(~scanf("%d%d%d",&n,&kin,&m))
    {
        if(!n&&!kin&&!m) break;
        memset(head,-1,sizeof(head));
        int j,k,tem;
        s=0; t=n+m+1;
        tot=0;
        double w=0,basic=0,sum=0;
        for(i=1;i<=m;i++)
        {
            scanf("%d",&cre[i]);
            w+=cre[i];
        }
        for(i=1;i<=m;i++)
        {
            scanf("%d",&bas[i]);
            if(bas[i]<60)
            {
                addedge(0,i,60-bas[i],tinf);
                sum=sum+(60-bas[i])*tinf;//
                bas[i]=60;
            }
            basic=basic+score[bas[i]]*cre[i];
            for(j=bas[i]+1;j<=100;j++)
                addedge(0,i,1,(score[j]-score[j-1])*cre[i]);
        }
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                scanf("%d",&tem);
                if(tem)
                    addedge(j,m+i,inf,0);
            }
        }
        for(i=m+1;i<t;i++)
            addedge(i,t,kin,0);
        double ans=mfmc();
        if(ans>=sum)
        {
            ans=(basic+(ans-sum))/w;
            printf("%.6lf\n",ans);
        }
        else printf("0.000000\n");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值