http://www.cnblogs.com/jianglangcaijin/archive/2012/10/06/2713375.html
题意:有m科课程需要学习,每个课程有一个基础分数,每学习该课程一个时间单位,该课程的分数就增加1分。现在有n天的学习时间,每天有K个单位时间,并且每天可以学习的课程是固定的,给出学分绩点的计算方式,求可以达到的最高的学分绩点,要求所有课程都要及格。
思路:设f(x,w)=(4.0-3.0*(100-x)*(100-x)/1600)*w;
(1)每一天向汇点连边,流量K,费用0;
(2)每门课根据要求与天连边,流量K,费用0;
(3)源点向每门课连边:若基础分s小于60,则与源点连一条流量60-s、费用INF的边,保证优先到达60,接着从源点与该课程连
40条流量分别为1,费用为f(x+1,p)-f(x,p)的边(40条代表61到100);若基础分s大于等于60,则连100-s条流量1、费用f(x+1,p)-f(x,p)的边。
求最大费用最大流。
完了统计从源点向各门课程的流量加上基础分,若还有小于60的,输出0;否则计算GPA。
#include <stdio.h> #include <iostream> #include <string.h> using namespace std; const int maxn=40000; const int MAXE=20000000; const int inf=1<<30; int head[maxn],s,t,cnt,n,m; double ans; int pre[maxn]; bool vis[maxn]; double d[maxn]; int q[MAXE]; int nn,kk,mm; int credit[300],bas[300]; struct Edge { int u,v,c,cap,next; double w; }edge[MAXE]; void addedge(int u,int v,double w,int c) { edge[cnt].u=u; edge[cnt].v=v; edge[cnt].w=w; edge[cnt].c=c; edge[cnt].cap=c; edge[cnt].next=head[u]; head[u]=cnt++; edge[cnt].v=u; edge[cnt].u=v; edge[cnt].w=-w; edge[cnt].cap=0; edge[cnt].c=0; edge[cnt].next=head[v]; head[v]=cnt++; } int SPFA() { int l,r; memset(pre,-1,sizeof(pre)); memset(vis,0,sizeof(vis)); for(int i=0;i<=t;i++) d[i]=-inf*1.0; d[s]=0; l=0;r=0; q[r++]=s; vis[s]=1; while(l<r) { int u=q[l++]; vis[u]=0; for(int j=head[u];j!=-1;j=edge[j].next) { int v=edge[j].v; if(edge[j].c>0&&d[u]+edge[j].w>d[v]) //最大费用最大流 { d[v]=d[u]+edge[j].w; pre[v]=j; if(!vis[v]) { vis[v]=1; q[r++]=v; } } } } if(d[t]==-inf*1.0) return 0; return 1; } void MCMF() { int flow=0; while(SPFA()) { int u=t; int mini=inf; while(u!=s) { if(edge[pre[u]].c<mini) mini=edge[pre[u]].c; u=edge[pre[u]].u; } flow+=mini; u=t; ans+=d[t]*mini; while(u!=s) { edge[pre[u]].c-=mini; edge[pre[u]^1].c+=mini; u=edge[pre[u]].u; } } } double fun(int x,int w) { return (4.0-3.0*(100-x)*(100-x)/1600)*w; } int main() { while(1) { int i,j; int xx; scanf("%d%d%d",&nn,&kk,&mm); if(nn==0&&mm==0&&kk==0) break; s=0;t=nn+mm+1; cnt=0; for(i=0;i<=t;i++) head[i]=-1; for(i=1;i<=mm;i++) scanf("%d",&credit[i]); for(i=1;i<=mm;i++) scanf("%d",&bas[i]); for(i=1;i<=nn;i++) addedge(i+mm,t,0,kk); for(i=1;i<=nn;i++) for(j=1;j<=mm;j++) { scanf("%d",&xx); if(xx) addedge(j,i+mm,0,kk); } double prev,next; for(i=1;i<=mm;i++) { if(bas[i]<60) { addedge(s,i,1.0*inf,60-bas[i]); prev=fun(60,credit[i]); for(j=61;j<=100;j++) { next=fun(j,credit[i]); addedge(s,i,next-prev,1); prev=next; } } else { prev=fun(bas[i],credit[i]); for(j=bas[i]+1;j<=100;j++) { next=fun(j,credit[i]); addedge(s,i,next-prev,1); prev=next; } } } MCMF(); for(i=head[0];i!=-1;i=edge[i].next) bas[edge[i].v]+=(edge[i].cap-edge[i].c); int sumcre=0; for(i=1;i<=mm;i++) sumcre+=credit[i]; double sumw=0; for(i=1;i<=mm;i++) { if(bas[i]<60) break; sumw+=fun(bas[i],credit[i])/sumcre; } if(i<=mm) sumw=0; printf("%.6lf ",sumw); } return 0; }