UVa 11212 Editing a Book

title: ‘UVa 11212 Editing a Book’
categories: UVa
date: 2015-12-16 20:30:00
tags: [IDA*]


Description

You have n equal-length paragraphs numbered 1 to n. Now you want to arrange them in the order
of 1, 2, … , n. With the help of a clipboard, you can easily do this: Ctrl-X (cut) and Ctrl-V (paste) several times. You cannot cut twice before pasting, but you can cut several contiguous paragraphs at the same time - they’ll be pasted in order.
For example, in order to make {2, 4, 1, 5, 3, 6}, you can cut 1 and paste before 2, then cut 3 and
paste before 4. As another example, one copy and paste is enough for {3, 4, 5, 1, 2}. There are two ways to do so: cut {3, 4, 5} and paste after {1, 2}, or cut {1, 2} and paste before {3, 4, 5}.

Input

The input consists of at most 20 test cases. Each case begins with a line containing a single integer n
(1 < n < 10), thenumber of paragraphs. The next line contains a permutation of 1, 2, 3, … , n. The
last case is followed by a single zero, which should not be processed.

Output

For each test case, print the case number and the minimal number of cut/paste operations.

Sample

input.txt
6
2 4 1 5 3 6
5
3 4 5 1 2
0

output.txt
Case 1: 2
Case 2: 1

Solution

n不是特别大,所以可以乱搞,这个剪切和粘贴的操作,我直接O(n^4)来搞的也没什么问题。至于剪枝,比如永远不破坏已经拼好的一段连续序列是很显然的,这个用来剪枝效果还不错。
当然比较神的是这个A*,我们发现每次剪切最多只能减少三个不正确的后继,所以我们可以先统计出当前状态有多少不正确的后继h,d为当前深度,maxd为深度上限,若3*d+h>3*maxd,则可以剪枝了。

Code

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

#define maxn 10+5

using namespace std;

int s[maxn];
int n;

int check(){
    int res=0;
    for(int i=1;i<n;i++)
        if(s[i]!=s[i+1]-1) res++;
    return res;
}

bool dfs(int t,int lim){
    int c=check();
    if(c==0) return true;
    if(3*t+c>3*lim) return false;
    int tmp[maxn],temp[maxn]={0};
    memcpy(tmp,s,sizeof(s));
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            if(s[j+1]!=s[j]+1 && s[i-1]!=s[i]-1){
                for(int k=0;k<i-1;k++){
                    int tail=0;
                    for(int l=1;l<=k;l++) temp[++tail]=s[l];
                    for(int l=i;l<=j;l++) temp[++tail]=s[l];
                    for(int l=k;l<i;l++) temp[++tail]=s[l];
                    for(int l=j+1;l<=n;l++) temp[++tail]=s[l];
                    for(int l=1;l<=n;l++) s[l]=temp[l];
                    bool ctrl=dfs(t+1,lim);
                    memcpy(s,tmp,sizeof(tmp));
                    if(ctrl) return true;
                }
                for(int k=j+1;k<=n;k++){
                    int tail=0;
                    for(int l=1;l<i;l++) temp[++tail]=s[l];
                    for(int l=j+1;l<=k;l++) temp[++tail]=s[l];
                    for(int l=i;l<=j;l++) temp[++tail]=s[l];
                    for(int l=k+1;l<=n;l++) temp[++tail]=s[l];
                    for(int l=1;l<=n;l++) s[l]=temp[l];
                    bool ctrl=dfs(t+1,lim);
                    memcpy(s,tmp,sizeof(tmp));
                    if(ctrl) return true;
                }
            }
    return false;
}

int main(){
    int T=0;
    while(scanf("%d",&n)==1 && n!=0){
        printf("Case %d: ",++T);
        for(int i=1;i<=n;i++)
            scanf("%d",&s[i]);
        bool f=true;
        for(int i=1;i<n;i++)
            if(s[i]!=s[i+1]-1){
                f=false;
                break;  
            }
        if(f){
            puts("0");
            continue;
        }
        for(int lim=1;23333;lim++){
            if(dfs(0,lim)){
                printf("%d\n",lim); 
                break;
            }
        }
    }
    return 0;   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值