UVA 11214 Editing a Book

9 篇文章 0 订阅

题目链接:

UVA 11214 Editing a Book

题目大意:

你有一篇由n(2<=n<=9) 个自然段组成的文章,希望将他们排列成1, 2, 3, …,n.可以用剪切和粘贴快捷键来完成任务.每次可以剪切一段连续的自然段,粘贴时按照顺序粘贴,注意,剪切板只有一个,所以不能连续剪切两次,只能剪切和粘贴交替.

例如,为了将{2, 4, 1, 5, 3, 6} 变为升序,可以剪切1将其放到2前,然后剪切3将其放到4前.再如,对于排列{3, 4, 5, 1, 2},只需一次剪切和一次粘贴,即可将{3, 4, 5}放在{1, 2}后,或将{1, 2, }放在{3, 4, 5}前.求最少操作次数

思路:

-----------------摘自算法竞赛,入门经典

策略一:
每次只剪切一段连续的数字.例如,不要剪切24这样的数字不连续的片段.
策略二:
假设剪切片段的第一个数字为a,最后一个数字为b,要么把这个片段粘贴倒a-1 的下一个位置,要么粘贴倒b+1的前一个位置.
策略三:
永远不要"破坏"一个已经连续的排列的数字片段.例如不要把 1234 中的2, 3剪切出来,

三种策略都能缩小状态空间,但是他们都不是正确的,很对程序都无法得到"54321"这的正确结果3(54321->32541->34125->12345)
本题可以用idea*算法求解,不难发现n<=9时最多只需8步,因此深度上限8.
IDEA*的关键在于启发函数,考虑后继不正确的数字个数h,可以证明每次剪切是H最多减少3,因此当3d+h>3maxd时可以剪枝,其中d为当前深度,maxd为的深度限制.

/*************************************************************************
        > File Name: uva_11212.cpp
      > Author: dulun
      > Mail: dulun@xiyoulinux.org
      > Created Time: 2016年04月15日 星期五 18时01分35秒
 ************************************************************************/

//Rey
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;

const int maxn = 9;

int n,a[maxn];

inline bool check()
{
    for(int i = 1; i < n; i++){
        if(a[i] <= a[i-1]) return false;
    }
    return true;
}

inline int h()
{
    int cnt = 0;
    for(int i = 1; i < n; i++)
        if(a[i] != a[i-1]+1) cnt++;
    return cnt;
}

int maxd;
const int intsz = sizeof(int);
const int asz = sizeof(a);
bool dfs(int d)
{
    if(3*d + h() > 3*maxd) return false;
    if(check()) return true;

    int old[maxn];//保存a
    memcpy(old,a,asz);
    int b[maxn];//剪切板

    for(int i = 0; i < n; i++) if( i == 0 || old[i] != old[i-1] + 1) //策略3 选择尽量长的连续片段 剪切的起点
    for(int j = i; j < n; j++) { //终点 和 策略2不同选取片段可以不连续
        while(j+1 < n && old[j+1] == old[j] + 1)j++;//策略3
        memcpy(b,old+i,intsz*(j-i+1));
        //剪切移动片段
        for(int k = j+1;k < n;k++){//由于对称性,只要往后贴就行了
            while(k+1 < n && old[k+1] == old[k] + 1)k++;//策略3 不破坏
            memcpy(a+i,old+j+1,intsz*(k-j));
            memcpy(a+i+k-j,b,intsz*(j-i+1));
            if(dfs(d+1))return true;
            //恢复
            memcpy(a,old,asz);
        }
    }
    return false;
}

inline int solve()
{
    if(check())return 0;
    for(maxd = 1; maxd < 5 ;maxd++)
        if(dfs(0)) return maxd;
    return 5;
}
int main()
{
   int Cas = 0;
    while(~scanf("%d",&n)&&n) {
        for(int i = 0; i < n; i++)
            scanf("%d",a+i);
        int ans = solve();
        printf("Case %d: %d\n",++Cas,ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值