485MS G++
#include <stdio.h>
#include <string.h>
#define MAX_L 10010
int realPos2optimalPosMap[MAX_L];
int waitingExchangedMap[MAX_L];
int optimalPos2realPosMap[MAX_L];
int OPTime;
int M;
int K;
struct Breaker{
int pos;
int requirePos;
int optimalPos;
};
typedef struct Breaker Breaker;
Breaker deadLockBreakerPos = {-999, -999};
int getUnusedPos() {
for (int i = 1; i <= K; i++) {
if (!realPos2optimalPosMap[i]) {
return i;
}
}
return 0;
}
int dfs(int optimalPos, int realPos) {
// printf("D0 %d %d\n", optimalPos, realPos);
if (optimalPos == realPos) {
return 1;
} else {
if (!realPos2optimalPosMap[optimalPos]) { // if the real pos cluster has no map top optimal, it is free.
OPTime++;
printf("%d %d\n", realPos, optimalPos);
realPos2optimalPosMap[optimalPos] = optimalPos;
realPos2optimalPosMap[realPos] = 0;
optimalPos2realPosMap[optimalPos] = optimalPos;
// optimalPos2realPosMap[realPos] = realPos;
return 1;
} else {
int pos = realPos2optimalPosMap[optimalPos]; // the data in current optimasPos's correspond pos in optimal array.
if (pos == realPos) {
OPTime += 3;
int unUsedPos = getUnusedPos();
// printf("exchanged %d %d\n", optimalPos, realPos);
printf("%d %d\n", optimalPos, unUsedPos);
printf("%d %d\n", realPos, optimalPos);
printf("%d %d\n", unUsedPos, realPos);
realPos2optimalPosMap[optimalPos] = optimalPos;
realPos2optimalPosMap[realPos] = realPos;
optimalPos2realPosMap[optimalPos] = optimalPos;
optimalPos2realPosMap[realPos] = realPos;
} else if (!waitingExchangedMap[optimalPos]){
waitingExchangedMap[realPos] = 1;
dfs(pos, optimalPos);
OPTime++;
printf("%d %d\n", realPos, optimalPos);
realPos2optimalPosMap[optimalPos] = optimalPos;
optimalPos2realPosMap[optimalPos] = optimalPos;
realPos2optimalPosMap[realPos] = 0;
// optimalPos2realPosMap[realPos] = realPos;
waitingExchangedMap[realPos] = 0;
if (deadLockBreakerPos.pos > 0 && deadLockBreakerPos.requirePos == realPos) {
OPTime++;
printf("%d %d\n", deadLockBreakerPos.pos, realPos);
// optimalPos2realPosMap[deadLockBreakerPos.pos] = deadLockBreakerPos.pos;
realPos2optimalPosMap[deadLockBreakerPos.pos] = 0;
realPos2optimalPosMap[realPos] = realPos;
optimalPos2realPosMap[realPos] = realPos;
deadLockBreakerPos.pos = -999;
deadLockBreakerPos.requirePos = -999;
}
return 1;
} else { // deadlock, break it
int freePos = getUnusedPos();
OPTime++;
printf("%d %d\n", realPos, freePos);
realPos2optimalPosMap[realPos] = 0;
realPos2optimalPosMap[freePos] = optimalPos;
deadLockBreakerPos.pos = freePos;
deadLockBreakerPos.requirePos = optimalPos;
optimalPos2realPosMap[optimalPos] = freePos;
// OPTime++;
// printf("V2 %d %d\n", realPos, optimalPos);
// realPos2optimalPosMap[optimalPos] = optimalPos;
// realPos2optimalPosMap[realPos] = 0;
// optimalPos2realPosMap[optimalPos] = optimalPos;
return 1;
}
}
}
}
void getOptimal() {
// printf("M %d\n", M);
for (int i = 1; i <= M; i++) {
dfs(i, optimalPos2realPosMap[i]);
}
if (!OPTime) {
printf("No optimization needed\n");
}
}
int main() {
K = 0;
M = 0;
int fileNum = 0;
while(scanf("%d %d", &K, &fileNum) != EOF) {
deadLockBreakerPos.pos = -999;
deadLockBreakerPos.requirePos = -999;
memset(realPos2optimalPosMap, 0, sizeof(realPos2optimalPosMap));
memset(optimalPos2realPosMap, 0, sizeof(optimalPos2realPosMap));
memset(waitingExchangedMap, 0, sizeof(waitingExchangedMap));
OPTime = 0;
int insertPos = 1;
for (int i = 0; i < fileNum; i++) {
int num = 0;
scanf("%d", &num);
for (int j = 0; j < num; j++) {
int val = 0;
scanf("%d", &val);
realPos2optimalPosMap[val] = insertPos;
optimalPos2realPosMap[insertPos++] = val;
}
}
M = insertPos-1;
getOptimal();
}
}
920K 485MS G++
搞懂了就是水题,但是第一次会很绕,颇有种 多层寻址+锁的感觉,感觉能和计算机间接寻址和多线程死锁扯上关系。
期间正好是周末,心不在焉,卡了半天,最后又因为两个标记数组最初是char(只记录又或者没有,实际应该是int,因为后来里面记录的其实是位置,<=10000,char溢出),
贡献了好几次RE..........
题目要求的是将当前的文件存储序列转化成最佳的存储访问序列,并且只需进行最小的文件块读取转存次数.
比如:
两个文件, 访问顺序是文件1(i个cluster),文件2(j个cluster). 那么最佳存储方式是 文件1数据顺序存储在磁盘的1~i cluster,文件2数据顺序在i+1 ~ i+1+j.
题目给出的文件的存储位置是任意的,最优访问序列第i个cluster的可以实际存储在磁盘的任意位置j,
这样就要求进行相应的转存操作(将某处的数据挪到另一处)使得存储在j的数据最终存储在i(该数据的最优位置)处。
最直观的想一下,如果存储在位置i的数据最终位置是j, 那么首先应该做的就是查看j处是否被别的数据所占据:
可以用dfs(i, j)表示将数据从i挪到j处。
case1: 第j处没有被别的数据占用,很好,直接将第i处的数据移动到第j处即可,这也是最优化的方案。
case2:第j处已经被别的数据所占用了,那么这时候,要进一步查看 j 处 所存储的数据的最优位置k,进一步递归调用dfs(j, k),在将j 挪到k以后,j就空出来了,
就可已经将i的数据挪到j了。 case2看起来似乎很简单(在大多数情况下,确实),但是却有一个致命的问题,设想一个最简单的case:
i处数据的最优位置是j, 而j处数据的最优位置是i, 如果按照上面的步骤,将会出现dfs(i,j) -> dfs(j, i) -> dfs(i, j) -> dfs(j,i)->...的无限递归调用,实际中最终造成栈溢出,segment fault, 就在这里贡献了一次RE......
上面这个情况非常类似于多线程的死锁,两个过程各自等待对方释放资源,那么,这种情况下,就需要打破这个死锁,
为了能够实现这个目的,引入了两个辅助变量:
一个是记录每个位置的waiting情况的waitingExchangedMap数组, waitingExchangedMap[i]记录此位置i是否在等待自己的最优位置j被腾出来(在递归调用链中,很可能在很早之前的i已经在等待j被挪出来了,如果放任不管,就会无限调用),用法就是:在dfs(i, j)时,如果位置j被占用,那么需要位置j的数据被挪到自己的最优位置k上,如果这时候发现waitingExchangedMap[j] == 1,即在之前的调用链中,j处数据已经在等待自己最优位置被挪出来(即dfs(j,k)已经被调用过),那么这时候,就不能再调用dfs(j,k),而应该打破这个死锁,将i的数据转存到其他的空位置T,用另外一个辅助变量记录此位置T,并且同时记录此位置T是在等待哪个位置(这里是j),然后直接返回,函数一路返回,直到返回到dfs(j, k),在将数据从j挪到最优的k以后,检测T是否有缓存打破死锁时存储的位置(不一定发生,因为这种循环调用不一定发生),同是看T等待的位置是否是j,如果T等待的就是j,那么还需要将T的数据挪到j处,这样,就实现了最优化存储,同时也避免了死锁.
这道题虽然不难,但是却将函数的调用堆栈结构和变量寻址展示的淋漓尽致,很值得好好咀嚼., 两个映射数组(优化位置数据的实际存储位置map 和 实际存储数据对应的最优化位置map)一度将我搞的很晕,而因为这两个数组又承担了至关重要的表示作用,所以中间觉得深陷泥潭,最后还是使用了将 变量的实际含义明确化 的思维来对code进行了check, 深深感到这种 含义明确所带来的力量.
上面提到的打破死锁其实也算是搜索树的一种剪枝,只不过这里的枝不是已经确定不是解或最优解的枝,而是已经经过的枝,这也是本题相对搜索树的一个特别之处,理论上,搜索树在一直向下走的时候是不会回到自己之前经过的状态的(图会),但是该题因为特殊的结构,导致了其实还会经过之前经过的状态,其实这样也不精确,因为状态也不完全相同,waitingExchangedMap这些状态已经变了。