POJ 1033 磁盘文件碎片整理 模拟题 栈应用

以后一定要细心,不能再犯这个低级的错误,把WA控制在最低范围内

参考了http://www.cnblogs.com/damacheng/archive/2010/09/24/1833983.html的题目分析

题目大意:你要写一个OS,要实现磁盘碎片整理的功能。磁盘分为N个簇,一个文件可以占用K个簇,(1 <= K < N <= 10000),给出各个文件的占用磁盘的情况,也就是一个文件占用了哪些簇,想要进行碎片整理,就是把这些簇按顺序整理到磁盘的最顶部,例如给出示例:

  文件1:2 3 11 12,占用了4个簇,编号为1-4。
  文件2:7,占用了1个簇,编号为5。

  文件3:18 5 10,占用了3个簇,编号为6-8。

  初始状态是这样的,0表示未占用:

  簇号:  1 2 3 4 5 6 7 8 910 11 12 13 14 15 16 17 18

  逻辑编号:0 1 2 07 0 5 00 8 34 00 0 0 0 6

  一共整理到最后,磁盘的情况最后是这样的:

  簇号:  1 2 3 4 5 6 7 8 910 11 12 13 14 15 16 17 18

  逻辑编号:1234567800000 0 0 000

  写一个程序得到整理好碎片最少需要多少步操作,并把这些操作打印出来。比如说第1个簇的内容放到第2个簇,打印出1 2。操作的定义是这样的:把一个簇的内容放到另个一个簇中,算是一步操作。

  注意这里是Special Judge,意思是只要答案符合要求就行了,不必和SAMPLE中的OUTPUT一样也可以AC。

  怎么才能找到最少的步数呢?我想了半天也没怎么想出来,于是看了看DISCUSS,总结以下:

  遍历整个磁盘,设i为当前遍历的簇的编号,clusters为整个磁盘,clusters[i]表示第i个簇是否被占用,被哪个编号的文件片段占据。

  (1) 如果clusters[i]为0,也就是未被使用,不进行处理。

  (2) 如果clusters[i]为i,也就是已经到了整理好的状态,不进行处理。

  (3) 如果clusters[i]不满足1和2,则又有两种情况。

    情况一:磁盘使用情况成链:如图所示:

    簇号:  1 2 3 4 5 6...    

    逻辑编号:5 04230 ...

    第1个簇被第5个文件片断占据,第5个簇又被第3个文件片段占据,第3个簇又被第4个文件片段占据,第4个簇又被第2个文件片断占据,第2个簇未被占据。算法就很简单了,按照簇被访问的反方向:

    clusters[2] = clusters[4],clusters[4] = clusters[3],clusters[3] = clusters[5],

    clusters[5] = clusters[1],最后clusters[1] = 0。怎么样反方向呢,使用一个栈就好了。

    情况二:磁盘使用情况成环:如图所示:

    簇号:  1 2 3 4 5 6...    

    逻辑编号:514230 ...

    这种情况跟情况一差不多,只是最后clusters[2]指向了第1个簇,这样就形成了一个环,这里只是需要额外处理一下,就像交换2个变量一样,先在从磁盘末尾找到1个空的簇,因为题目保证至少有一个空的簇,先把clusters[2]放到这个空的簇中,然后再执行情况1中的操作,最后再把空的簇的值赋给clusters[1]就好了。最后注意一点,如果操作次数为0,则需要输出一行信息。


Source Code

Problem: 1033 User: yangliuACMer
Memory: 420K Time: 704MS
Language: C++ Result: Accepted
#include <iostream> #include <stack> using namespace std; int clusters[10001]; int clusters_num, file_num; int counter = 1, move_num = 0;//文件片段的其实编号和操作的总数 int next, i, j, t, n; stack<int> s; void deal(){ while(!s.empty()){ t = s.top(); cout<<t<<" "<<next<<endl; clusters[next] = clusters[t]; next = t; s.pop(); move_num++; } } void work(){ for(i = 1; i <= clusters_num; i++){ if(clusters[i] == i){//刚好放在了目标位置,无需移动 continue; } else if(clusters[i] != 0){ s.push(i); next = clusters[i]; bool is_circled = false;//下面判断是否出现了环 while(true){ if(clusters[next] == i){ is_circled = true; break; } else if(clusters[next] == 0){ is_circled = false; break; } s.push(next); next = clusters[next];//有用数组模仿链表的思想 } if(is_circled == true){//有环的情况 for(j = clusters_num; j >=0; j--){ if(clusters[j] == 0) break;//从后向前找一个空位置,为解开环做准备 } cout<<next<<" "<<j<<endl; clusters[j] = clusters[next]; deal(); clusters[next] = clusters[j];//把开始的结点移动回去 clusters[j] = 0; cout<<j<<" "<<next<<endl; } else {//没有环的情况 deal(); clusters[next] = 0; } } } if(move_num == 0) cout<<"No optimization needed"<<endl;//开始因为输出格式中needed写成need 而WA一次,太粗心了!!! } int main(){ cin>>clusters_num>>file_num; for(i = 0; i < file_num; i++){ cin>>n; for(j = 0; j < n; j++){ cin>>t; clusters[t] = counter++; } } work(); return 0; }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值