题意:磁盘n个块,有m个文件,各自被分割成许多块分散在磁盘之中,要求通过最少移动次数使得第1个文件的占1,2,3...f1块,第二个文件占f1+1,f1+2...f1+f2块。。。

题解:dfs(k)为将k位置的文件移到它该去的地方,标记dfs过的点,若发生重复则说明有环,就让当前dfs点移到最大一个空闲块,否则,必定能将链还原。

View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=10005;
 6 int w[N],p[N],n,num;//w[i]:i位置上有一个应该移去w[i]的碎片;p[i]:应该放在i位置的碎片现在的位置
 7 bool vis[N];
 8 bool dfs(int k)//把现在在k位置的碎片复原,true代表有环
 9 {
10     vis[k]=true;
11     int tp=w[k];
12     if(w[tp]==0)
13     {
14         printf("%d %d\n",k,tp);
15         w[k]=0;
16         w[tp]=tp;
17         p[tp]=tp;
18         return false;
19     }
20     else if(vis[tp])
21     {
22         for(int i=n;i>=0;i--)
23         {
24             if(w[i]==0)
25             {
26                 printf("%d %d\n",k,i);
27                 w[i]=w[k];
28                 w[k]=0;
29                 p[w[i]]=i;
30                 return true;
31             }
32         }
33     }
34     else
35     {
36         bool flag=dfs(tp);
37         printf("%d %d\n",k,w[k]);
38         w[k]=0;
39         w[tp]=tp;
40         p[tp]=tp;
41         return flag;
42     }
43 }
44 int main()
45 {
46     while(scanf("%d%d",&n,&num)!=EOF)
47     {
48         int pos=1,nu,tp;
49         memset(w,0,sizeof(w));
50         bool flag=true;
51         for(int i=1;i<=num;i++)
52         {
53             scanf("%d",&nu);
54             for(int j=0;j<nu;j++)
55             {
56                 scanf("%d",&tp);
57                 w[tp]=pos;
58                 p[pos]=tp;
59                 if(pos!=tp)
60                     flag=false;
61                 pos++;
62             }
63         }
64         if(flag)
65         {
66             printf("No optimization needed\n");
67             continue;
68         }
69         for(int i=1;i<pos;i++)
70         {
71             if(p[i]!=i)
72             {
73                 memset(vis,false,sizeof(vis));
74                 if(dfs(p[i]))
75                     i--;
76             }
77         }
78     }
79     return 0;
80 }