UVA - 11212 Editing a Book (IDA*)

给你一个长度为n(n<=9)的序列,每次可以将一段连续的子序列剪切到其他地方,问最少多少次操作能将序列变成升序。

本题最大的坑点在于让人很容易想到许多感觉挺正确但实际却不正确的策略来避开一些看似“不好”的操作,比如:不破坏连续的片段,拼接时总是保证多出一个比它左边的数大一的数,等等。而实际上这些策略都是错误的,所以只能暴搜。“将一段连续的子序列剪切到其他地方”等价于“交换两段连续的片段”,因此可以枚举三个划分点i,j,k进行搜索,但这样的话dfs树会很宽大,会TLE。考虑到最少只需要n-1步就能完成任务,因此可以用IDA*来优化,设估价函数h代表当前序列中不正确的数的个数,则每次操作最多将h减少3,用这个来剪枝。

“交换片段”的操作的分界点的选取是个难点,如果自己边界处理的能力不够好的话,不断调参去试也不失为一个好的方法(人体模拟退火)。此外,也可以用C++的库函数copy来简化代码。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=10+2;
 5 int a[N],b[N],n,ka;
 6 int calh() {
 7     int ret=0;
 8     for(int i=0; i<n-1; ++i)if(a[i]+1!=a[i+1])ret++;
 9     if(a[n-1]!=n)ret++;
10     return ret;
11 }
12 
13 void cut(int l,int m,int r) {
14     copy(a+m,a+r,b);
15     copy(a+l,a+m,b+(r-m));
16     copy(b,b+(r-l),a+l);
17 }
18 
19 bool dfs(int dep,int mxd) {
20     int h=calh();
21     if(h==0)return 1;
22     if(3*dep+h>3*mxd)return 0;
23     for(int i=0; i<n; ++i)
24         for(int k=i+1; k<=n; ++k)
25             for(int j=i+1; j<k; ++j) {
26                 cut(i,j,k);
27                 if(dfs(dep+1,mxd))return 1;
28                 cut(i,i+(k-j),k);
29             }
30     return 0;
31 }
32 
33 int IDAStar() {for(int mxd=0;; ++mxd)if(dfs(0,mxd))return mxd;}
34 
35 int main() {
36     while(scanf("%d",&n)&&n) {
37         printf("Case %d: ",++ka);
38         for(int i=0; i<n; ++i)scanf("%d",&a[i]);
39         printf("%d\n",IDAStar());
40     }
41     return 0;
42 }

 

转载于:https://www.cnblogs.com/asdfsag/p/10364693.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值