UVa 11212 编辑书稿(dfs+IDA*)

https://vjudge.net/problem/UVA-11212

题意:给出n个自然段组成的文章,将他们排列成1,2...,n。每次只能剪切一段连续的自然段,粘贴时按照顺序粘贴。

思路:状态空间的搜索问题。

首先介绍一下IDA*,它属于DFS,在DFS遍历的时候,设定一个深度上限maxd,当前结点n的深度为g(n),乐观估价函数为h(n),则当g(n)+h(n)>maxd时应           该剪枝。这样的算法就是IDA*。

在这道题目中,由于最多就9个数,所以最多只需要剪切8次肯定是可以完成升序排列的。所以最大深度可以从1开始一直到8,依次去寻找是否能成功。

在这题中最重要的剪枝就是考虑后继不正确的数字个数h,可以证明每次剪切时h最多减少3,因此如果3*(maxd-d)<h,则可以直接剪枝。因为此时即使一直遍历到限定深         度maxd,也无法将h的个数减为0。另外也还有许多地方可以剪枝,下面的代码中我有仔细介绍。

 1 #include<iostream>
 2 #include<string>
 3 #include<cstring>
 4 using namespace std;
 5
 6 int n;
 7 int a[12];
 8
 9 bool goal() //判断是否已达到最终状态
10 {
11     for (int i = 0; i < n - 1; i++)
12     {
13         if (a[i]>a[i + 1]) return false;
14     }
15     return true;
16 }
17
18 int h()  //计算后继不正确的个数
19 {
20     int number = 0;
21     for (int i = 0; i < n-1; i++)
22     {
23         if (a[i + 1] != a[i] + 1)
24             number++;
25     }
26     if (a[n - 1] != n) number++;
27     return number;
28 }
29
30 bool dfs(int d, int maxd)
31 {
32     if (3 * d + h()>3 * maxd)  return false; //剪枝
33     if (goal())   return true;
34     int pre[12];  //保存原来序列
35     int cut[12];  //保存剪切后的序列
36     for (int i = 0; i < n; i++)  //枚举,i为剪切起点,j为剪切终点
37     {
38         if (i == 0 || a[i]  != a[i - 1]+1) //剪枝,不破坏连续的数字片段
39         {
40             for (int j = i; j < n; j++)
41             {
42                 while (a[j + 1] == a[j] + 1)  j++; //剪枝,如果一个数字片段已经连续,则不要去破坏它
43                 memcpy(pre, a, sizeof(a));
44                 int cnt = 0;
45                 for (int k = 0; k < n; k++)  //保存剪切后的序号
46                 {
47                     if (k<i || k>j)
48                         cut[cnt++] = a[k];
49                 }
50                 for (int k = 0; k <= cnt; k++)   //枚举,依次插入到第k个位置之前
51                 {
52                     int cnt2 = 0;
53                     for (int t = 0; t < k; t++)  a[cnt2++] = cut[t];
54                     for (int t = i; t <= j; t++) a[cnt2++] = pre[t];
55                     for (int t = k; t < cnt; t++) a[cnt2++] = cut[t];
56                     if (dfs(d + 1, maxd))      return true;  //继续深搜
57                     memcpy(a, pre, sizeof(pre));  //如果失败,则恢复a[]的原来的数字片段
58                 }
59             }
60         }
61     }
62     return false;
63 }
64
65 int solve()
66 {
67     if (goal())    return 0;
68     for (int i = 1; i < 9; i++)   //最多只需要进行8次dfs即可获得最终状态
69     {
70         if (dfs(0, i))  return i;
71     }
72 }
73
74 int main()
75 {
76     //freopen("D:\\txt.txt", "r", stdin);
77     int kase = 0;
78     while (cin >> n && n)
79     {
80         memset(a, 0, sizeof(a));
81         for (int i = 0; i < n; i++)
82             cin >> a[i];
83         int ans=solve();
84         cout << "Case " << ++kase << ": " << ans << endl;
85     }
86 }
时间: 2024-10-25 19:07:24

UVa 11212 编辑书稿(dfs+IDA*)的相关文章

Uva 11212 编辑书稿(迭代加深搜索)

题意: 给定N个数的序列, 希望将它排列成1~N, 可以用剪切.粘贴来完成任务, 每次可以剪切一段连续的自然段, 粘贴时按照顺序粘贴. #include <bits/stdc++.h> #define rep(i,a,b) for(int i = a; i < b; i++) #define _rep(i,a,b) for(int i = a; i <= b; i++) using namespace std; const int maxn = 12; int n, maxd; i

UVa 11212 Editing a Book (IDA* &amp;&amp; 状态空间搜索)

题意:你有一篇n(2≤n≤9)个自然段组成的文章,希望将它们排列成1,2,-,n.可以用Ctrl+X(剪切)和Ctrl+V(粘贴)快捷键来完成任务.每次可以剪切一段连续的自然段,粘贴时按照顺序粘贴.注意,剪贴板只有一个,所以不能连续剪切两次,只能剪切和粘贴交替.例如,为了将{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}前. 分析

uva 11212 - Editing a Book(迭代加深搜索 IDA*) 迭代加深搜索

迭代加深搜索 自己看的时候第一遍更本就看不懂..是很水,但智商捉急也是没有办法的事情. 好在有几个同学已经是做过了这道题并且对迭代加深搜索的思路有了一定的了解,所以在某些不理解的地方询问了一下他们的见解, 真的是很有帮助,也许自己想要想很久才能想明白,还会很痛苦,稍微问一下别人的想法,点上一个方向,剩下的自己就能想得明白了. 迭代加深. 把answer(需要的步数或其他)在主函数里面从零往上递加,此之谓 "层数",亦可谓之"深度".用书上的话就是: 从小到大枚举深度

Editing a Book UVA - 11212 IDA*

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,

[2016-02-27][UVA][11212][Editing a Book]

[2016-02-27][UVA][11212][Editing a Book] 时间:2016-02-26 19:38:44 星期五 题目编号:UVA 11212 题目大意:给定长度为n(值为1~n)的序列,求把该序列 复制/粘贴 成1~n 的排列最少步数 分析: 状态空间搜索,但是每次状态转移的方式有多种,可能会T, 发现,最多 操作 的次数:n~1的序列,每次操作 长度为1的连续段,那么需要 n-1 次操作 可以用IDA*算法 剪枝,每次操作之后,每个数字的后继数字不正确的数目h,最多减少

UVa 572 Oil Deposits(DFS)

 Oil Deposits  The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots.

UVA 10318 - Security Panel dfs 剪枝

UVA 10318 - Security Panel dfs 剪枝 ACM 题目地址:UVA 10318 - Security Panel 题意: 这题跟点灯的题目很像,点灯游戏选择一盏灯时会让它以及四周的灯改变状态. 但是我们有特殊的开开关技巧,它给出了改变状态的位置,而不是四周都改变. 问你从全部关着变成全部开着的最小开关步骤. 分析: 很明显,在一个位置上点两次或更多次是没有必要的,所以一个位置只有选择与不选择,用dfs即可,但如果暴力所有可能,复杂度是2^25,会超时,所以要剪枝. 由于

EOJ1154 CAN YOU DFS? IDA*搜索算法

题目:EOJ1154 CAN YOU DFS?  IDA*搜索算法 Description 在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数. 如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的. 对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好. 如: 19/45=1/3 + 1/12 + 1/180 19/45=1/3 + 1/15 + 1/45 19/45=1/

uva 12253 - Simple Encryption(dfs)

题目链接:uva 12253 - Simple Encryption 题目大意:给定K1,求一个12位的K2,使得KK21=K2%1012 解题思路:按位枚举,不且借用用快速幂取模判断结果. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const ll ite=(1<<20)-1; ll N; /* l