- 传送门 -
2238 - Fixed Partition Memory Management
Time limit: 9.000 seconds
| Root |
|
(题面见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;
}