题意:有N天M门课,每天只能选k个班级学习。每个班级只能学习一门课并且可以提升这门课的1分。
给出M门课的绩点和基本的成绩。
然后给一个N*M的矩阵,第i行第j列为1则代表第i天第j门课可以学习,否则不能学习。
给出计算GPA的两个式子
发现GPA取决于p,而p取决于x,对p(x)观察后发现是一个上升函数(导函数>0),而p(x+1)-p(x)也是一个上升函数(导函数>0),那么就可以拆边了,一分拆一边,<60的就不需要拆边了。
代码:
//author: CHC //First Edit Time: 2014-10-28 12:24 //Last Edit Time: 2014-10-28 13:22 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <set> #include <vector> #include <map> #include <queue> #include <set> #include <algorithm> #include <limits> using namespace std; typedef long long LL; const int MAXN=1e+4; const int MAXM=1e+5; const int INF= numeric_limits<int>::max(); const LL LL_INF= numeric_limits<LL>::max(); struct Edge { int from,to,ci,cost,next; Edge(){} Edge(int _from,int _to,int _ci,int _cost,int _next):from(_from),to(_to),ci(_ci),cost(_cost),next(_next){} }e[MAXM]; int head[MAXN],tot; int q[MAXM]; int dis[MAXN],pre[MAXN],rec[MAXN],vis[MAXN]; inline void init(){ memset(head,-1,sizeof(head)); tot=0; } inline void AddEdge1(int u,int v,int ci,int cost){ e[tot]=Edge(u,v,ci,cost,head[u]); head[u]=tot++; e[tot]=Edge(v,u,0,-cost,head[v]); head[v]=tot++; } inline bool spfa(int S,int T,LL &cost,LL &flow){ int i,h=0,t=0; for(i=0;i<=MAXN;i++){ dis[i]=INF; vis[i]=false; } q[h]=S; dis[S]=0; vis[S]=true; while(h<=t){ int u=q[h++]; vis[u]=false; for(i=head[u];~i;i=e[i].next){ int v=e[i].to; if(e[i].ci>0&&dis[v]>dis[u]+e[i].cost){ dis[v]=dis[u]+e[i].cost; pre[v]=u; rec[v]=i; if(!vis[v]){ vis[v]=1; q[++t]=v; } } } } if(dis[T]==INF)return false; int minn=INF; for(i=T;i!=S;i=pre[i]){ if(e[rec[i]].ci<minn) minn=e[rec[i]].ci; } for(i=T;i!=S;i=pre[i]){ //cost+=minn*e[rec[i]].cost; e[rec[i]].ci-=minn; e[rec[i]^1].ci+=minn; } cost+=dis[T]*minn; flow+=minn; return true; } inline void mincostmaxflow(int S,int T,LL &cost,LL &flow){ while(spfa(S,T,cost,flow)); } int credit[MAXN],score[MAXN]; int main() { /* int pre=6400-3*40*40,now; for(int i=61;i<=100;i++){ now=6400-3*(100-i)*(100-i); printf("%d\n",now-pre); now=pre; } */ int n,k,m,x; while(~scanf("%d%d%d",&n,&k,&m)){ if(n==0&&k==0&&m==0)break; int s=n+m+m; int t=s+1; init(); for(int i=1;i<=m;i++)scanf("%d",&credit[i]); for(int i=1;i<=m;i++){ scanf("%d",&x); score[i]=x; int pre,now; if(x<60){ AddEdge1(i+n,t,60-x,-INF); pre=6400-3*40*40; x=60+1; } else { pre=6400-3*(100-x)*(100-x); x++; } for(;x<=100;x++){ now=6400-3*(100-x)*(100-x); AddEdge1(i+n,t,1,-(now-pre)*credit[i]); pre=now; } } for(int i=1;i<=n;i++){ AddEdge1(s,i,k,0); for(int j=1;j<=m;j++){ scanf("%d",&x); if(x){ AddEdge1(i,j+n,k,0); } } } LL cost=0,flow=0; mincostmaxflow(s,t,cost,flow); for(int i=head[t];~i;i=e[i].next){ score[e[i].to-n]+=e[i].ci; //printf("%d -> %d ci:%d\n",e[i].from,e[i].to,e[i].ci); } int flag=0; for(int i=1;i<=m;i++){ if(score[i]<60)flag=1; //printf("%d:%d\n",i,score[i]); } if(flag)puts("0.000000"); else { int sum=0,wi=0; for(int i=1;i<=m;i++){ sum+=(6400-3*(100-score[i])*(100-score[i]))*credit[i]; wi+=credit[i]; } printf("%.6lf\n",1.0*sum/1600.0/wi); } } return 0; }