HDU 4406 GPA(费用流)

GPA

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


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(0th 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

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

题解:这是一道费用流,大概就是每一分连一条边,费用就是收益,优先每门课及格,所以小于60分连的边的流量是离60分的数值费用都是无穷大,大于60分每一分连一条边流量是一费用是收益。求最大费用最大流即可(费用设为负数则可求最小费用最大流),若无穷大的数目足够,则全部可以及格,则费用就是答案


#include 
#include 
#define typef int
#define typec double

using namespace std;

const int E = 888888;
const int N = 10008;
const typef inff = 0x3f3f3f3f;
const typec infc = 1e8;
const typec inf = 1e4;
const int maxn = 88;

double w[maxn];
double ww, res;

int mp[maxn][maxn];
int base[maxn];
int n, m, kk;
int cc;

double getp(int id, int x){
	if(x < 60) return 0;
	return (4 - 3.0 * (100 - x) * (100 - x) / 1600) * w[id] / ww;
}

double cal(int id, int x){
	//printf("getp   %f\n", getp(id, x));
	return getp(id, x + 1) - getp(id, x);
}

struct network{
	int nv, ne, pnt[E], nxt[E];
	int vis[N], que[N], head[N], pv[N], pe[N];
	typef flow, cap[E]; typec cost, dis[E], d[N];

	void addedge(int u, int v, typef c, typec w){
		//printf("%d   %d   %d   %f\n", u, v, c, w);
		pnt[ne] = v; cap[ne] = c;
		dis[ne] = +w; nxt[ne] = head[u]; head[u] = ne++;
		pnt[ne] = u; cap[ne] = 0;
		dis[ne] = -w; nxt[ne] = head[v]; head[v] = ne++;
	}

	double mincost(int src, int sink){
		int i, k, f, r; typef mxf;

		for(flow = 0, cost = 0; ; ){
			memset(pv, -1, sizeof(pv));
			memset(vis, 0, sizeof(vis));
			for(i = 0; i < nv; i++) d[i] = infc;
			d[src] = 0; pv[src] = src; vis[src] = 1;

			for(f = 0, r = 1, que[0] = src; r != f; ){
				i = que[f++]; vis[i] = 0;
				//if(N == f) f = 0;
				for(k = head[i]; k != -1; k = nxt[k]){
					if(cap[k] && dis[k] + d[i] < d[pnt[k]]){
						d[pnt[k]] = dis[k] + d[i];
						if(0 == vis[pnt[k]]){
							vis[pnt[k]] = 1;
							que[r++] = pnt[k];
							//if(N == r) r = 0;
						}
						pv[pnt[k]] = i; pe[pnt[k]] = k;
					}
				}
			}
			if(-1 == pv[sink]) break;

			for(k = sink, mxf = inff; k != src; k = pv[k]){
				if(cap[pe[k]] < mxf) mxf = cap[pe[k]];
			}
			flow += mxf; cost += d[sink] * mxf;

			for(k = sink; k != src; k = pv[k]){
				cap[pe[k]] -= mxf; cap[pe[k] ^ 1] += mxf;
			}
		}
		//printf("cost   %f\n", cost);

		return cost;
	}

	void build(int v){
		nv = v; ne = 0;
		memset(head, -1, sizeof(head));
		// 0 = src, 1 = sink
		// 2 ~ m + 1 = p, m + 2 ~ m + n + 1 = day
		for(int i = 1; i <= m; i++){
			if(base[i] < 60){
				addedge(0, 1 + i, 60 - base[i], -inf);
				cc += 60 - base[i];
				res += getp(i, 60);
			}
			else{
				res += getp(i, base[i]);
			}
			for(int j = base[i] < 60 ? 60 : base[i]; j < 100; j++){
				//printf("%d\n", j);
				addedge(0, 1 + i, 1, -cal(i, j));
			}
		}
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= m; j++){
				if(!mp[i][j]) continue;
				addedge(j + 1, m + 1 + i, kk, 0);
			}
			addedge(m + 1 + i, 1, kk, 0);
		}
	}
}g;

int main(){

	//n days, m course, kk class every day
	while(scanf("%d%d%d", &n, &kk, &m), n + kk + m){
		cc = ww = res = 0;
		for(int i = 1; i <= m; i++){
			scanf("%lf", w + i);
			ww += w[i];
		}
		for(int i = 1; i <= m; i++) scanf("%d", base + i);
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= m; j++){
				scanf("%d", &mp[i][j]);
			}
		}
		g.build(n + m + 8);
		//printf("res   %f\n", res);
		res += -g.mincost(0, 1);
		//printf("%f    %d   %f\n", res, cc, -inf * cc);
		res = res - inf * cc;
		printf("%f\n", res < 0 ? 0 : res);
	}

	return 0;
}



你可能感兴趣的:(图论,ACM之路(c/c++),网络流,最小费用最大流,图论)