初看这道题,方法肯定用搜索。。不过想想,搜索的宽度和深度又是个问题。所以就用迭代加深
什么是迭代加深,从加深来看,就是一开始只搜第一层,深度超过1时强行退出,第二次只搜2层,就这样一直往下,直到找到最优解。
那么为什么要用他呢??迭代加深的优点:1.不会像DFS一样一直找到叶子节点(转牛角尖),2.不会像BFD内存爆掉(相当于改进过的DFS,可以一层一层搜索,只不过顺序还是DFS)
也可以说,迭代加深的思想之一为,枚举答案ANS,再讲ANS带入DFS,看是否满足,而一般的搜索恰好相反,为直接BFS枚举答案。
要用迭代加深,那么最好要有一个好的启发式函数(剪枝用)。
代码我准备分为一个个函数来讲。
首先是判断找到答案:
bool is_sorted() {
for(int i = 0; i < n-1; i++)
if(a[i] >= a[i+1]) return false;//也就是查看是否已排好序
return true;
}
第二,启发式函数(可以看做是)
int h() {
int cnt = 0;
for(int i = 0; i < n-1; i++)
if(a[i]+1 != a[i+1]) cnt++;
if(a[n-1] != n) cnt++;
return cnt;
}
h()这个函数用处是取出顺序不正确的数字的个数,通过公式3d+h()>3max来判断当前搜索的最大层数是否合理
当3d+h()>3max时,我们的最大搜索层数maxd就小了,所以返回,再把maxd加大。
三,DFS函数
bool dfs(int d, int maxd) {
if(d*3 + h() > maxd*3) return false;//上面说的剪枝
if(is_sorted()) return true;//递归边界
int b[maxn], olda[maxn];//临时数组,和旧的a数组
memcpy(olda, a, sizeof(a));//复制数组
for(int i = 0; i < n; i++)
for(int j = i; j < n; j++) {//这里是枚举剪切的头和尾,i代表从哪里开始剪切,j表示剪切到哪里,故j要从i开始
int cnt = 0;//计数
for(int k = 0; k < n; k++)
if(k < i || k > j) b[cnt++] = a[k];//这句话作用是将未被剪切的字母放到B数组中
for(int k = 0; k <= cnt; k++) {//这里就是枚举插入的位置
int cnt2 = 0;
for(int p = 0; p < k; p++) a[cnt2++] = b[p];//从第K位插入,则将K前的元素先加到数组中
for(int p = i; p <= j; p++) a[cnt2++] = olda[p];//将被剪切的部分插入
for(int p = k; p < cnt; p++) a[cnt2++] = b[p];//最后加入K后的元素,相当于把b数组分为两部分,先加第一部分,在加被剪切部分,最后加入第二部分
if(dfs(d+1, maxd)) return true;//继续搜
memcpy(a, olda, sizeof(a));//还原数组
}
}
return false;
}
第四,枚举答案部分,这部分相对简单
int solve() {
if(is_sorted()) return 0;//这里不用解释
int max_ans = 5; // 经过测试,当n<=9时,ans<=5
for(int maxd = 1; maxd < max_ans; maxd++)//设置搜索最大层数,其实变相的也是在枚举答案
if(dfs(0, maxd)) return maxd;//因为可以保证第一次搜到答案时一定是最短路径,而搜索层数就是路径的长度
return max_ans;//搜不到返回最大答案
}
好吧,最后一个主函数了
int main() {
int kase = 0;
while(scanf("%d", &n) == 1 && n) {
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
printf("Case %d: %d\n", ++kase, solve());
}
return 0;
}
输入输出不解释
最后发完整代码
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 9;
int n, a[maxn];
bool is_sorted() {
for(int i = 0; i < n-1; i++)
if(a[i] >= a[i+1]) return false;
return true;
}
int h() {
int cnt = 0;
for(int i = 0; i < n-1; i++)
if(a[i]+1 != a[i+1]) cnt++;
if(a[n-1] != n) cnt++;
return cnt;
}
bool dfs(int d, int maxd) {
if(d*3 + h() > maxd*3) return false;
if(is_sorted()) return true;
int b[maxn], olda[maxn];
memcpy(olda, a, sizeof(a));
for(int i = 0; i < n; i++)
for(int j = i; j < n; j++) {
// cut
int cnt = 0;
for(int k = 0; k < n; k++)
if(k < i || k > j) b[cnt++] = a[k];
for(int k = 0; k <= cnt; k++) {
int cnt2 = 0;
for(int p = 0; p < k; p++) a[cnt2++] = b[p];
for(int p = i; p <= j; p++) a[cnt2++] = olda[p];
for(int p = k; p < cnt; p++) a[cnt2++] = b[p];
if(dfs(d+1, maxd)) return true;
memcpy(a, olda, sizeof(a));
}
}
return false;
}
int solve() {
if(is_sorted()) return 0;
int max_ans = 5;
for(int maxd = 1; maxd < max_ans; maxd++)
if(dfs(0, maxd)) return maxd;
return max_ans;
}
int main() {
int kase = 0;
while(scanf("%d", &n) == 1 && n) {
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
printf("Case %d: %d\n", ++kase, solve());
}
return 0;
}