• [题解] 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;
    }
    
  • 相关阅读:
    【uva 1442】Cav(算法效率)
    【uva 10600】ACM Contest and Blackout(图论--次小生成树 模版题)
    【bzoj2429】[HAOI2006]聪明的猴子(图论--最小瓶颈生成树 模版题)
    【uva 534】Frogger(图论--最小瓶颈路 模版题)
    【poj 1988】Cube Stacking(图论--带权并查集)
    【uva 12174】Shuffle(算法效率--滑动窗口)
    关于最小生成树 Kruskal 和 Prim 的简述(图论)
    2019牛客暑期多校训练营(第五场) maximum clique 1
    左偏树/可并堆 学习笔记
    树的计数 Prüfer编码与Cayley公式 学习笔记
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7482164.html
Copyright © 2020-2023  润新知