Uva11212 编辑书稿(Editing a book,IDE算法)

迭代加深法, 可以解决一些看起来更适合用BFS和回溯法的题,但是BFS和回溯法浪费空间,所以迭代加深法更能解决问题。

分析:

由于编辑的特殊性,只要没达到升序这个条件就可以永远不停的搜下去,最特别的还是那些的错误的路径,也就是说假如一开始就选择了错误的剪切方式,之后这条错误的路会一直往后延伸。。。

所以采用IDA*解法,IDA*相比与普通的dfs或bfs,多了一个估值函数,比如我从这一层到第n层,每层最多能干多少活,假如剩下的活到第n层怎么都做不完,就不要往下搜了。像这道题,每层最多有三个数字的后缀改变,也就是说最多能使三个数字的后继正确。

证明:设当前有四个数字 a b c d ,如果把b移到c后面,则改变了a、b、c三个数的后继

所以每次搜索前,当h>(maxd-d)*3 时剪枝,这里maxd-d即剩余搜索次数;

搜索的核心就是截取 [i, j] 插入剩余序列的第k个数字前

关于这IDA*的减枝策略,每遍历一个深度的时候,进行判断:

当前局面的估价函数值 + 当前深度 > 预定义最大搜索深度 

的时候进行减枝。

就能这道题而言,假如我们定义一个数字是不是位置正确:这个数 x 是否 等于 这个数 后面的 数 y - 1,也就是

x =? y - 1,如果等于,说明这个数位置正确,如果不等于,说明这个数位置错误,位于最后一个位置的数的时候,判断他是不是等于n,比如:4,5,6,1,2,3 这个序列存在2个不正确位置数,分别是6(后面是1)和3(3不等于6)。

下面进行这道题的减枝的分析(也叫做启发函数),当你改变一个区间的位置,你会改变3个数的位置的正确性

比如 1,2,3,4,5,6.序列,你把2,3移动到6后面,那么1的后面变成了5, 而 6的后面编程了2,而3的后面变成 空了,所以每次移动一个区间,最多可以改变3个数的正确性,也就是说,对于这道题如果遍历到了一个深度, (还能遍历的深度 - 当前深度) *3 < 不正确数字的个数,那么就没有必要继续遍历了,因为往后你就是全把这些数字该对了也无法达到理想状态,即使再遍历也是做无用功。

知道这个之后时间复杂度的问题就得到解决了,下面我们只需要每次枚举该步的所有移动就可以了。

移动的话,实际就是2个相邻区间的交换,比如A B C D(字母代表区间),将A移动到C后面,也可以看成A 和(BC)互换,所以实质就是相邻区间的互换。

#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++) {
     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 = 8;
  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;
}



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值