[题解] uvalive 2238 Fixed Partition Memory Management(二分图最佳完美匹配)

- 传送门 -

 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=239

2238 - Fixed Partition Memory Management

Time limit: 9.000 seconds
| Root | Submit Problem Stats
uDebug Download as PDF |

(题面见pdf)

 

- 题意 -

 不想写...
 就是有 (n) 个程序, (m) 个内存, 每个程序(以 (u) 为例)对内存的要求分为 (k_u) 级, 每一级有一个内存点 (S_i), 时间 (T_i), 对于某一段内存(大小为 (x) )来说, 设 (s_{i-1} leq x < s_i) , 则程序 (u) 在该内存内运行的时间为(T_{i-1}).
 再考虑一个内存中运行多个程序, 出现这种情况时, 最后运行的程序对总时间的贡献为本身, 倒数第二个要把本身的时间乘二, 然后依此类推. 例如我们在内存 (C) 中依次运行程序 (x, y, z)(对应时间为(T_{C_x},T_{C_y},T_{C_z})), 则该内存中的总时间为(T_{C_x}*3,T_{C_y}*2,T_{C_z}*1).
 求一个使总时间最小的方案.
 要求输出平均时间(总时间/总程序数), 每个程序在哪个内存中运行, 运行的起始,终止时间(就是真实时间,与上面的玄学式子没关系).
 

- 思路 -

 参考: http://blog.csdn.net/u014679804/article/details/46725083
 
 我也不知道这种东西怎么想得粗来, 直接照本(题解)宣科咯...
 
 X 集放程序, Y 集放每一个内存从第一个程序到第 n 个程序的位置(这样写不太好理解, 就是说 X 中每一个程序要可以对应任一内存的第 1 - n 个运行的程序).
 对于程序 x 作为第 y 个内存运行的第 c 个程序的情况(设时间是t, y 总运行的程序数是 k), 它的贡献是(k - c + 1) * t, 因为 k 是未知的, 这样显然不方便, 我们可以从最后一个程序开始考虑, 考虑程序 x 作为第 y 个内存运行的倒数第 c 个程序, 那么它的贡献就是c * t, c , t 都是已知的, 可以建边啦, 于是我们把内存的 n 个程序位置视作倒数来建图好啦.
 因为 km 要找最大值, 所以我们把边权什么的都取个反, 然后注意一下输出.
 注意把程序往后放总是更优的, 所以得到的答案一定是内存的最后几个位置有程序.
 
 细节见代码.
 

- 代码 -

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 55;
const int M = 15;
const int inf = 0x3f3f3f3f;

struct program {
	int k;
	int S[M], T[M];
	int B[N * M];
}W[N];

struct ans {
	int id, l, r;
}A[N];

int LX[N], LY[N * M], SLACK[N * M], X[N], Y[N * M];
int LINE[N * M], MEM[M], TI[M];
int n, m, tmp, cas;

bool match(int u) {
	X[u] = 1;
	
	for (int i = 1; i <= n * m; ++i) {
		if (!Y[i]) {
			if (LX[u] + LY[i] == W[u].B[i]) {
				Y[i] = 1;
				if (LINE[i] == 0 || match(LINE[i])) {
					LINE[i] = u;
					return true;
				}
			}
			else
				SLACK[i] = min(SLACK[i], LX[u] + LY[i] - W[u].B[i]);
		}
	}
	return false;
}

void km() {
	for (int i = 1; i <= n; ++i) {
		LX[i] = -inf;
		for (int j = 1; j <= n * m; ++j)
			LX[i] = max(LX[i], W[i].B[j]);
	}

	for (int j = 1; j <= n * m; ++j) {
		LINE[j] = 0;
		LY[j] = 0;
	}

	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n * m; ++j)
			SLACK[j] = inf;
		
		while (true) {
			memset(X, 0, sizeof (X));
			memset(Y, 0, sizeof (Y));
			
			if (match(i)) break;
			
			tmp = inf;
			for (int j = 1; j <= n * m; ++j)
				if (!Y[j]) tmp = min(tmp, SLACK[j]);
			
			for (int j = 1; j <= n; ++j)
				if (X[j]) LX[j] -= tmp;
				
			for (int j = 1; j <= n * m; ++j)
				if (Y[j]) LY[j] += tmp;
		}
	}
}

void print() {
	if (cas != 1) printf("
");
	printf("Case %d
", cas);
	int ans = 0;
	for (int i = 1; i <= n; ++i) ans += LX[i];
	for (int i = 1; i <= n * m; ++i) ans += LY[i];
	printf("Average turnaround time = %.2lf
", - ans * 1.0 / n);
	memset(TI, 0, sizeof(TI));
	for(int i = 1; i <= m; ++i){
		int p;
		for(p = 1; p <= n; ++p) if(!LINE[(i - 1) * n + p]) break;
		for(p = p - 1; p >= 1; --p){
			int x = LINE[(i - 1) * n + p];
			A[x].id = i;
			A[x].l = TI[i];
			TI[i] -= W[x].B[(i - 1) * n + p] / p;//不要忽略 /p , 建边时是乘上了位置的
			A[x].r = TI[i];
		}
	}
    for(int i = 1; i <= n; ++i)
        printf("Program %d runs in region %d from %d to %d
", i, A[i].id, A[i].l, A[i].r);
}

int main() {
	
	while (scanf("%d%d", &m, &n) != EOF && n && m) {
		
		cas ++;
		
		for (int i = 1; i <= m; ++i)
			scanf("%d", &MEM[i]);
		
		for (int ii = 1; ii <= n; ++ii) {
			scanf("%d", &W[ii].k);
			for (int i = 1; i <= W[ii].k; ++i)
				scanf("%d%d", &W[ii].S[i], &W[ii].T[i]);
		}

		for (int ii = 1; ii <= n; ++ii) {
			for (int i = 1; i <= m; ++i) {
				if (MEM[i] < W[ii].S[1]) tmp = inf;
				else {
					int pos;
					for (pos = 1; pos <= W[ii].k; ++pos)
						if (W[ii].S[pos] > MEM[i]) break;
					tmp = W[ii].T[pos - 1]; //找到程序ii在内存i中的运行时间
				}
				
				for (int j = 1; j <= n; ++j) {
					if (tmp == inf) W[ii].B[(i - 1) * n + j] = -inf;
					else W[ii].B[(i - 1) * n + j] = -j * tmp; //对ii作为内存i的倒数第j个程序来建边,注意取反
				}
			}
		}

		km();
		
		print();
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/Anding-16/p/7482164.html