https://vjudge.net/problem/UVA-11212
题意:给出n个自然段组成的文章,将他们排列成1,2...,n。每次只能剪切一段连续的自然段,粘贴时按照顺序粘贴。
思路:状态空间的搜索问题。
首先介绍一下IDA*,它属于DFS,在DFS遍历的时候,设定一个深度上限maxd,当前结点n的深度为g(n),乐观估价函数为h(n),则当g(n)+h(n)>maxd时应 该剪枝。这样的算法就是IDA*。
在这道题目中,由于最多就9个数,所以最多只需要剪切8次肯定是可以完成升序排列的。所以最大深度可以从1开始一直到8,依次去寻找是否能成功。
在这题中最重要的剪枝就是考虑后继不正确的数字个数h,可以证明每次剪切时h最多减少3,因此如果3*(maxd-d)<h,则可以直接剪枝。因为此时即使一直遍历到限定深 度maxd,也无法将h的个数减为0。另外也还有许多地方可以剪枝,下面的代码中我有仔细介绍。
1 #include<iostream> 2 #include<string> 3 #include<cstring> 4 using namespace std; 5 6 int n; 7 int a[12]; 8 9 bool goal() //判断是否已达到最终状态 10 { 11 for (int i = 0; i < n - 1; i++) 12 { 13 if (a[i]>a[i + 1]) return false; 14 } 15 return true; 16 } 17 18 int h() //计算后继不正确的个数 19 { 20 int number = 0; 21 for (int i = 0; i < n-1; i++) 22 { 23 if (a[i + 1] != a[i] + 1) 24 number++; 25 } 26 if (a[n - 1] != n) number++; 27 return number; 28 } 29 30 bool dfs(int d, int maxd) 31 { 32 if (3 * d + h()>3 * maxd) return false; //剪枝 33 if (goal()) return true; 34 int pre[12]; //保存原来序列 35 int cut[12]; //保存剪切后的序列 36 for (int i = 0; i < n; i++) //枚举,i为剪切起点,j为剪切终点 37 { 38 if (i == 0 || a[i] != a[i - 1]+1) //剪枝,不破坏连续的数字片段 39 { 40 for (int j = i; j < n; j++) 41 { 42 while (a[j + 1] == a[j] + 1) j++; //剪枝,如果一个数字片段已经连续,则不要去破坏它 43 memcpy(pre, a, sizeof(a)); 44 int cnt = 0; 45 for (int k = 0; k < n; k++) //保存剪切后的序号 46 { 47 if (k<i || k>j) 48 cut[cnt++] = a[k]; 49 } 50 for (int k = 0; k <= cnt; k++) //枚举,依次插入到第k个位置之前 51 { 52 int cnt2 = 0; 53 for (int t = 0; t < k; t++) a[cnt2++] = cut[t]; 54 for (int t = i; t <= j; t++) a[cnt2++] = pre[t]; 55 for (int t = k; t < cnt; t++) a[cnt2++] = cut[t]; 56 if (dfs(d + 1, maxd)) return true; //继续深搜 57 memcpy(a, pre, sizeof(pre)); //如果失败,则恢复a[]的原来的数字片段 58 } 59 } 60 } 61 } 62 return false; 63 } 64 65 int solve() 66 { 67 if (goal()) return 0; 68 for (int i = 1; i < 9; i++) //最多只需要进行8次dfs即可获得最终状态 69 { 70 if (dfs(0, i)) return i; 71 } 72 } 73 74 int main() 75 { 76 //freopen("D:\txt.txt", "r", stdin); 77 int kase = 0; 78 while (cin >> n && n) 79 { 80 memset(a, 0, sizeof(a)); 81 for (int i = 0; i < n; i++) 82 cin >> a[i]; 83 int ans=solve(); 84 cout << "Case " << ++kase << ": " << ans << endl; 85 } 86 }