hdu 4406 GPA(最大费用流)

题意:距离考试有n天,现在有m个科目要复习,每天能有k个复习时间单元,每用一个时间单元复习,就能让所复习的科目分数增加一分,在保证不挂科的情况下,让绩点尽量高……


思路:这题除了费用的问题外,建图还是比较直观的,添加一个源点S和汇点T,每一天向T连一条容量为k,费用为0的弧,每个科目向对应能复习的天连一条容量为k,费用为0的弧,剩下就是S向科目连对应的弧。首先,为了保证不挂科,如果第i科基础分数小于60分,那么就从S向i连一条容量为60-分数,费用为inf的弧,这样在执行费用流的时候会优先填满这些弧,对于其他的分数x,我们可以算出x+1时增加的绩点,设f(x)为x对应的绩点的话,可以算一下f(x+1)-f(x),发现这是个减函数,因此,我们只需要对应会增加的分数,每一分连一条容量为1,费用为f(x)-f(x-1)的弧就好了。最后做一下最大费用流,其实这样建图也不会有环,因此费用取反做最小费用流也一样,最后检查一下是否还是有科目会挂就好了。


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100+10;
const int maxm=4000+10;
struct Edge
{
    int from,to,cap,flow,next;
    double cost;
    Edge(){};
    Edge(int ff,int tt,int cc,int fl,double co,int nx)
    {from=ff;to=tt;cap=cc;flow=fl;cost=co;next=nx;}
}edges[maxm<<1];
int head[maxn],a[maxn],p[maxn],nEdge;
bool inq[maxn];
double d[maxn];
void AddEdges(int from,int to,int cap,double cost)
{
    edges[++nEdge]=Edge(from,to,cap,0,cost,head[from]);
    head[from]=nEdge;
    edges[++nEdge]=Edge(to,from,0,0,-cost,head[to]);
    head[to]=nEdge;
}
bool spfa(int s,int t,int &flow,double &cost)
{
    memset(inq,0,sizeof(inq));
    memset(d,0,sizeof(d));
    a[s]=inf;p[s]=0;
    queue<int>q;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        inq[u]=false;
        for(int k=head[u];k!=-1;k=edges[k].next)
        {
            Edge e=edges[k];
            if(e.cap>e.flow&&d[e.to]<d[u]+e.cost)
            {
                d[e.to]=d[u]+e.cost;
                a[e.to]=min(a[u],e.cap-e.flow);
                p[e.to]=k;
                if(!inq[e.to]){inq[e.to]=true;q.push(e.to);}
            }
        }
    }
    if(d[t]==0) return false;
    flow+=a[t];
    cost+=a[t]*d[t];
    int u=t;
    while(u!=s)
    {
        edges[p[u]].flow+=a[t];
        edges[p[u]^1].flow-=a[t];
        u=edges[p[u]].from;
    }
    return true;
}
void mincost(int s,int t)
{
    int flow=0;double cost=0;
    while(spfa(s,t,flow,cost)) ;
}
int basicp[maxn],w[maxn],matrix[maxn][maxn],res[maxn];
double getpoint(int i,int x)
{
    return (4.0-3.0*(100-x)*(100-x)/1600.0)*w[i];
}
double solve(int S,int m)
{
    double ans=0;
    memset(res,0,sizeof(res));
    for(int k=head[S];k!=-1;k=edges[k].next)
    {
        Edge e=edges[k];
        res[e.to]+=e.flow;
    }
    for(int i=1;i<=m;++i)
    {
        res[i]+=basicp[i];
        if(res[i]<60) return 0;
        ans+=getpoint(i,res[i]);
    }
    return ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,k,m;
    while(~scanf("%d%d%d",&n,&k,&m))
    {
        if(n==0&&k==0&&m==0) break;
        memset(head,0xff,sizeof(head));
        nEdge=-1;
        for(int i=1;i<=m;++i)
            scanf("%d",&w[i]);
        for(int i=1;i<=m;++i)
            scanf("%d",&basicp[i]);
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                scanf("%d",&matrix[i][j]);
        int S=0,T=n+m+1,sum=0;
        for(int i=1;i<=m;++i)
        {
            if(basicp[i]<60) AddEdges(S,i,60-basicp[i],1e6);
            int j=max(basicp[i],60)+1;
            for(;j<=100;++j)
                AddEdges(S,i,1,getpoint(i,j)-getpoint(i,j-1));
            for(j=1;j<=n;++j)
                if(matrix[j][i]) AddEdges(i,m+j,k,0);
            sum+=w[i];
        }
        for(int i=1;i<=n;++i)
            AddEdges(m+i,T,k,0);
        mincost(S,T);
        double ans=solve(S,m)/sum;
        printf("%.6lf\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值