hdu4406 GPA 费用流

http://acm.hdu.edu.cn/showproblem.php?pid=4406

GPA

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 828    Accepted Submission(s): 288


Problem Description
GPA(Grade-Point Average) is one way to measure students’ academic performance in PKU. Each course has an integer credit, ranges from 1 to 99. For each course, you will get a score at the end of the semester, which is an integer ranges from 0 to 100. Then you can calculate the Grade-Point of this course with the following formula. (Your score is x and your Grade-Point is p, using real arithmetic)

Then you can get the GPA with the following formula (the Grade-Point of course i is p i, and the credit of course i is w i).

Now it is not far from the final exam, if you do not review, you can only get a basic score in each course.

You have n days to review. There are K classes in each day. For each class, only one course can be reviewed. After the review, your score in this course will exactly increase by 1. You can get more increment by spending more classes in this course. But the score may not exceed 100.

For some reasons, not any course can be reviewed in any class. Each day you can only review some of the courses.

Now you want your GPA to be as high as possible, and at the same time, you do not want to fail in any course. Please calculate the highest GPA you can get. 
 

Input
The input consists of several test cases. Each test case begins with 3 integers N (0<=N<=40), K(0<K<=20), M (0<M<=20), representing the number of days, the number of classes in each day and the number of courses. Next line contains M integers representing credits of each course and M integers representing basic scores of each course (0<=score<=100). Next N lines contain an N*M matrix, the j th element in i th row means whether you can review course j in i th day, 1 means you can review course j in i th day, 0 means you cannot. The Input ends with 0 0 0.
 

Output
For each test case, output the highest possible GPA, round to 6 digits after decimal point. If you have to fail a course, output 0.000000 instead.
 

Sample Input
  
  
2 10 3 1 1 2 50 60 90 1 1 0 1 0 1 2 20 4 1 1 1 1 50 50 50 40 1 1 1 0 0 0 0 1 0 0 0
 

Sample Output
  
  
2.757813 0.000000
 

Source

题意:有m科课程需要学习,每个课程有一个基础分数,每学习该课程一个时间单位,该课程的分数就增加1分。现在有n天的学习时间,每天有K个单位时间,并且每天可以学习的课程是固定的,给出学分绩点的计算方式,求可以达到的最高的学分绩点,要求所有课程都要及格。

分析:费用和流量平方成正比的最大费用最大流,大白p366有模型。

每天向汇点连边,流量为K,费用0;每门课向符合要求的天连边,流量K费用0。

源点向每门课连边,为了保证每门课及格,连向s<60分的课流量60-s,费用-INF,然后每门课连min(100-s,40)条流量为1费用为f(x,p)-f(x+1,p)的边,可以发现每条边的费用随着x增大是递增的(负的),这样就保证增广的时候是按分数依次的。这里的f值可以化为整数,就避免了处理精度。

跑最小费用最大流,然后计算每门课的基础上加上各自的流量,如果还有小于60输出0.

/**
 * @author neko01
 */
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define clr(a) memset(a,0,sizeof a)
#define clr1(a) memset(a,-1,sizeof a)
#define dbg(a) printf("%d\n",a)
typedef pair<int,int> pp;
const double eps=1e-9;
const double pi=acos(-1.0);
const int INF=0x3f3f3f3f;
const LL inf=(((LL)1)<<61)+5;
const int maxn=505;
const int M=400010;
struct Edge{
    int to,next,cap,cost;
    Edge(int _to=0,int _next=0,int _cap=0,int _cost=0){
        to=_to;next=_next;cap=_cap;cost=_cost;
    }
}e[M];
int head[maxn],tol;
int pre[maxn];
double dist[maxn];
bool vis[maxn];
int N; //点数
void init(int n)
{
    N=n;
    tol=0;
    clr1(head);
}
void add(int u,int v,int cap,double cost)
{
    e[tol]=Edge(v,head[u],cap,cost);
    head[u]=tol++;
    e[tol]=Edge(u,head[v],0,-cost);
    head[v]=tol++;
}
bool spfa(int s,int t)
{
    queue<int>q;
    for(int i=0;i<=N;i++)
    {
        dist[i]=INF;
        vis[i]=false;
        pre[i]=-1;
    }
    dist[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=e[i].next)
        {
            int v=e[i].to;
            if(e[i].cap&&dist[v]>dist[u]+e[i].cost)
            {
                dist[v]=dist[u]+e[i].cost;
                pre[v]=i;
                if(!vis[v]) vis[v]=true,q.push(v);
            }
        }
    }
    if(pre[t]==-1) return  false;
    return true;
}
void mincostflow(int s,int t,int &flow,int &cost)
{
    flow=cost=0;
    while(spfa(s,t))
    {
        int Min=INF;
        for(int i=pre[t];i!=-1;i=pre[e[i^1].to])
            if(Min>e[i].cap) Min=e[i].cap;
        for(int i=pre[t];i!=-1;i=pre[e[i^1].to])
            e[i].cap-=Min,e[i^1].cap+=Min,cost+=e[i].cost*Min;
        flow+=Min;
    }
}
int w[25];
int sc[25];
int a[45][25];
int fun(int x,int w)
{
    return (6400-3*(100-x)*(100-x))*w;
}
int main()
{
    int n,k,m;
    while(~scanf("%d%d%d",&n,&k,&m))
    {
        if(n==0&&k==0&&m==0) break;
        int sum=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&w[i]);
            sum+=w[i];
        }
        for(int i=1;i<=m;i++)
            scanf("%d",&sc[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&a[i][j]);
        int s=0,t=n+m+1;
        init(t+1);
        for(int i=1;i<=m;i++)
        {
            if(sc[i]<60)
                add(s,i,60-sc[i],-INF);
            int j=max(sc[i],60);
            int pre=fun(j,w[i]);
            for(j++;j<=100;j++)
            {
                int now=fun(j,w[i]);
                add(s,i,1,pre-now);
                pre=now;
            }
            for(j=1;j<=n;j++)
                if(a[j][i])
                    add(i,j+m,k,0);
        }
        for(int i=1;i<=n;i++)
            add(i+m,t,k,0);
        int flow,cost;
        mincostflow(s,t,flow,cost);
        for(int i=head[s];i!=-1;i=e[i].next)
            sc[e[i].to]+=e[i^1].cap;
        int ans=0,i;
        for(i=1;i<=m;i++)
        {
            if(sc[i]<60) break;
            ans+=fun(sc[i],w[i]);
        }
        if(i!=m+1) ans=0;
        printf("%.6lf\n",1.0*ans/sum/1600);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值