编程之美——一摞烙饼的排序(暴搜+剪枝)

题目

分析

深度优先搜索遍历每一种情况,去翻转次数最小的,当然,还要加一些剪枝,毕竟O(nn)的时间复杂度。

代码

C风格

 1 /**** 前缀排序 ****/
 2 #include<stdio.h>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6
 7 const int maxn = 100 + 10;
 8 int n, arr[maxn];            //烙饼个数和烙饼数组
 9 int arr_cmp[maxn];
10 int arr_tmp[maxn];                //记录初始数组
11
12 int search_times = 0;        //总搜索次数
13 int max_swap;            //最小交换次数
14 int arr_swap[2 * maxn];        //最终翻转方案
15 int tarr_swap[2 * maxn];    //当前翻转方案
16
17 void Init()
18 {
19     for (int i = 0; i < n; i++)  arr_tmp[i] = arr_cmp[i] = arr[i];
20     sort(arr_cmp, arr_cmp + n);
21     max_swap = 2 * (n - 1);
22 }
23
24 void Reverse(int* arr,int start, int end)
25 {
26     for (int i = start, j = end; i < j; i++, j--)
27         swap(arr[i], arr[j]);
28 }
29
30 int LowerBound()
31 {
32     int ret = 0;
33     for (int i = 1; i < n; i++)
34         if (arr[i - 1] > arr[i])  ret++;
35     return ret;
36 }
37
38 bool IsSort()
39 {
40     for (int i = 0; i < n; i++)
41         if (arr_cmp[i] != arr[i])  return false;
42     return true;
43 }
44
45 void Search(int step)
46 {
47     search_times++;
48
49     if (IsSort())    //已排好序
50     {
51         if (step < max_swap)
52         {
53             max_swap = step;
54             for (int i = 0; i < max_swap;i++)  arr_swap[i] = tarr_swap[i];
55         }
56     }
57
58     if ((step + LowerBound()) > max_swap)  return;
59
60     for (int i = 1; i <= n; i++)
61     {
62         Reverse(arr,0, i);
63         tarr_swap[step] = i;
64         Search(step + 1);
65         Reverse(arr,0, i);
66     }
67 }
68
69 void Print()
70 {
71     printf("搜索次数:%d\n", search_times);
72     printf("翻转次数: %d\n", max_swap);
73     for (int i = 0; i < max_swap; i++)  printf("%d ", arr_swap[i]);
74     printf("\n具体的翻转情况:\n");
75     for (int i = 0; i < max_swap; i++)
76     {
77         Reverse(arr_tmp, 0, arr_swap[i]);
78         for (int j = 0; j < n; j++)  printf("%d ", arr_tmp[j]);
79         printf("\n");
80     }
81 }
82
83 int main()
84 {
85     scanf("%d", &n);
86     for (int i = 0; i < n; i++)  scanf("%d", &arr[i]);
87     Init();
88     Search(0);
89     Print();
90 }

C++风格

  1 #include<stdio.h>
  2 #include<cstring>
  3 #include<cassert>         //assert宏的原型定义在<assert.h>中
  4 #include<algorithm>
  5 using namespace std;
  6
  7 class laobing
  8 {
  9 private:
 10     int* m_CakeArray;        //烙饼信息数组
 11     int m_nCakeCnt;            //烙饼个数
 12     int m_nMaxSwap;            //最多翻转次数
 13     int* m_SwapArray;        //最终翻转烙饼位置
 14     int* m_ReverseCakeArray;        //当前烙饼数组
 15     int* m_ReverseCakeArraySwap;    //当前翻转烙饼位置
 16     int m_nSearch;            //搜索次数
 17
 18 public:
 19     laobing()
 20     {
 21         int m_CakeCnt = 0;
 22         int m_nMaxSwap = 0;
 23     }
 24     ~laobing()
 25     {
 26         if (m_CakeArray != NULL)  delete m_CakeArray;
 27         if (m_SwapArray != NULL)  delete m_SwapArray;
 28         if (m_ReverseCakeArray != NULL)  delete  m_ReverseCakeArray;
 29         if (m_ReverseCakeArraySwap != NULL)  delete m_ReverseCakeArraySwap;
 30     }
 31
 32     //计算烙饼翻转信息
 33     //pCakeArray    存储烙饼数组
 34     //nCakeCnt      烙饼个数
 35     void run(int* pCakeArray, int nCakeCnt)
 36     {
 37         Init(pCakeArray, nCakeCnt);
 38         m_nSearch = 0;
 39         Search(0);
 40     }
 41
 42     //输出交换过程
 43     void OutputArray(int* CakeArray, int nCakeCnt, int* m_SwapArray, int m_nMaxSwap)
 44     {
 45         for (int i = 0; i < m_nMaxSwap; i++)   //每一次交换
 46         {
 47             Reverse(CakeArray, 0, m_SwapArray[i]);
 48             for (int i = 0; i < nCakeCnt; i++)  printf("%d ", CakeArray[i]);
 49             printf("\n");
 50         }
 51     }
 52
 53     //输出烙饼具体的翻转情况
 54     void Output()
 55     {
 56         for (int i = 0; i < m_nMaxSwap; i++)  printf("%d ", m_SwapArray[i]);
 57         printf("\n");
 58         printf("Search Times: %d\n", m_nSearch);
 59         printf("Total Times: %d\n", m_nMaxSwap);
 60
 61         OutputArray(m_CakeArray, m_nCakeCnt, m_SwapArray, m_nMaxSwap);
 62     }
 63 private:
 64     void Init(int *pCakeArray, int nCakeCnt)
 65     {
 66         assert(pCakeArray != NULL);        //其作用是如果它的条件返回错误,则终止程序执行。
 67         assert(nCakeCnt > 0);
 68
 69         //初始化烙饼数组
 70         m_nCakeCnt = nCakeCnt;
 71         m_CakeArray = new int[m_nCakeCnt];
 72         assert(m_CakeArray != NULL);
 73         for (int i = 0; i < m_nCakeCnt; i++)
 74             m_CakeArray[i] = pCakeArray[i];
 75
 76         //设置最多交换次数
 77         m_nMaxSwap = UpBound(m_nCakeCnt);
 78
 79         //初始化交换数组结果
 80         m_SwapArray = new int[m_nMaxSwap];
 81         assert(m_SwapArray != NULL);
 82
 83         //初始化中间交换结果
 84         m_ReverseCakeArray = new int[m_nCakeCnt];
 85         for (int i = 0; i < m_nCakeCnt; i++)
 86             m_ReverseCakeArray[i] = m_CakeArray[i];
 87         m_ReverseCakeArraySwap = new int[m_nMaxSwap];
 88     }
 89
 90     //寻找当前翻转次数的上界
 91     //n个烙饼,翻转最大的n-2烙饼最多需要2*(n-2)次,剩下的2个最多1次,因而上限值为2*n-3,
 92     //因此,m_nMaxSwap初始值可以取2*n-3+1=2*n-2,这样每步与m_nMaxSwap的判断就可以取大等于号。
 93     int UpBound(int nCakeCnt)
 94     {
 95         return 2 * (nCakeCnt - 1);
 96     }
 97
 98     //寻找当前翻转次数的下界
 99     int LowerBound(int* pCakeArray, int nCakeCnt)
100     {
101         int ret = 0;
102
103         //根据当前数组的顺序信息来判断最少需要交换多少次
104         for (int i = 1; i < nCakeCnt; i++)
105         {
106             //判断位置相邻的两个烙饼是否是尺寸相邻的
107             int tmp = pCakeArray[i] - pCakeArray[i - 1];
108             if (tmp == 1 || tmp == -1)  continue;
109             else  ret++;
110         }
111         if (pCakeArray[nCakeCnt - 1] != nCakeCnt - 1)  ret++;  //判断下界时,如果最大的烙饼不在最后一个位置,则要多翻转一次
112         return ret;
113     }
114
115     //排序的主函数
116     void Search(int step)
117     {
118         m_nSearch++;
119         // // 估算这次搜索所需要的最小交换次数
120         int nEstimate = LowerBound(m_ReverseCakeArray, m_nCakeCnt);
121         if (step + nEstimate >= m_nMaxSwap)  return;        //剪枝
122
123         //如果已经排好序,直接输出
124         if (IsSort(m_ReverseCakeArray, m_nCakeCnt))
125         {
126             if (step < m_nMaxSwap)
127             {
128                 m_nMaxSwap = step;
129                 for (int i = 0; i < m_nMaxSwap; i++)  m_SwapArray[i] = m_ReverseCakeArraySwap[i];
130             }
131             return;
132         }
133
134         //递归进行翻转
135         for (int i = 1; i < m_nCakeCnt; i++)
136         {
137             Reverse(m_ReverseCakeArray, 0, i);
138             m_ReverseCakeArraySwap[step] = i;
139             Search(step + 1);    //递归
140             Reverse(m_ReverseCakeArray,0, i);        //回溯
141         }
142     }
143
144     //检查是否排好序
145     bool IsSort(int * pCakeArray, int nCakeCnt)
146     {
147         for(int i = 1;i < nCakeCnt;i++)
148             if (pCakeArray[i - 1] > pCakeArray[i])  return false;
149         return true;
150     }
151
152     //翻转烙饼信息
153     void Reverse(int* m_arr,int nBegin, int nEnd)
154     {
155         assert(nEnd > nBegin);
156
157         for (int i = nBegin, j = nEnd; i < j; i++, j--)
158             swap(m_arr[i], m_arr[j]);
159     }
160 };
161
162 const int maxn = 100 + 10;
163 int n, arr[maxn];
164
165 int main()
166 {
167     laobing lb;
168     laobing* p = new laobing();
169     scanf("%d", &n);
170     for (int i = 0; i < n; i++)  scanf("%d", &arr[i]);
171
172     p->run(arr, n);
173     p->Output();
174
175     lb.run(arr, n);
176     return 0;
177 }

参考链接:

https://blog.csdn.net/tianshuai1111/article/details/7659673

http://blog.sina.com.cn/s/blog_a2aa00d70101ewuf.html

https://blog.csdn.net/ML_algorithmResearch/article/details/50405548

原文地址:https://www.cnblogs.com/lfri/p/9938618.html

时间: 2024-08-29 06:11:53

编程之美——一摞烙饼的排序(暴搜+剪枝)的相关文章

编程之美之一摞烙饼的排序1

拿到这个问题, 第一反应是利用分治的算法思想, 每次把当前的最大的一块烙饼放到指定位置 ,这样的思想非常简单,实现也非常容易.但是这只是提供了,问题的一个可行解,看完书中的内容之后发现,题目中要求的是最优化的输出过程,我们的这种方法显然没有考虑到优化<-_->!! 其实,我觉得就算我看到了这个最优化输出的要求,估计也想不到书中的设计思想的了.过段时间,等自己把书中的思想忘掉之后,再看看能不能想到这种算法思想吧.这应该算是埋了个坑吧 <-_->!! 这里将自己的代码贴出来: // =

《编程之美》之一摞烙饼的排序

星期五的晚上,一帮同事在希格玛大厦附近的“硬盘酒吧”多喝了几杯,程序员多喝了几杯之后谈什么呢?自然是算法 问题.有个同事说: “我以前在餐厅打工,顾客经常点非常多的烙饼.店里的烙饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小 次序摆好---小的在上面,大的在下面.由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们 上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了.我后来想,这实际上是个有趣的排序问题:假设有n块大小 不一的摞饼,那最少要翻几次,才能达到大小有序的结果呢?”

编程之美学习笔记之 一摞烙饼的排序

编程之美书中讲的一摞烙饼的排序一题 这里无法用基本的排序方法对其排序,那么最直接的方法是找出N个数种最大者,将这通过两次翻转放置到最底部,然后处理N-1,N-2等,直到全部排序完,所以一共需要交换2(N-1)次 void reverse(int cakes[], int beg, int end) { int temp; while(beg < end){ temp = cakes[beg]; cakes[beg++] = cakes[end]; cakes[end--] = temp; } }

第1章 游戏之乐——一摞烙饼的排序

一摞烙饼的排序 问题:写出一个程序,对于n块大小不一的烙饼,输出最优化的翻饼过程?要保证烙饼按照大小次序摆好——小的在上面,大的在下面. 分析与解法: 用java实现的代码如下: package chapter1youxizhilePrefixSorting; import java.util.Scanner; /** * 一摞烙饼的 * @author DELL * */ public class PrefixSorting { private int[] m_CakeArray; //烙饼信

编程之美——1.3一摞烙饼的排序

//自己看到这个问题后的解法#include<iostream> using namespace std; typedef int status; //将一个数组p的坐标0到i的元素调个头 status diao_tou(int *p,int i) { if(i==0) return 1; for(int j=0;j<=((i-1)/2);j++) { int temp; temp=p[i-j]; p[i-j]=p[j]; p[j]=temp; } return 1; } int mai

编程之美—烙饼排序问题(JAVA)

一.问题描述 星期五的晚上,一帮同事在希格玛大厦附近的"硬盘酒吧"多喝了几杯.程序员多喝了几杯之后谈什么呢?自然是算法问题.有个同事说:"我以前在餐      馆打工,顾客经常点非常多的烙饼.店里的饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好--小的在上面,大的在下面.由于我      一只手托着盘子,只好用另一只手,一次抓最上面的几块饼,把它们上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了.我后来想,这实际上是个     有趣的排序问题:假设有n块大小不一

leetcode&amp;编程之美——博文目录

leetcode刷题整理: 1——Two Sum(哈希表hashtable,map) 2——Add Two Numbers(链表) 3——Longest Substring Without Repeating Characters(set,哈希表,两个指针) 9——Palindrome Number (数学问题) 11——Container With Most Water(两个指针) 12——Integer to Roman(string,数学问题) 13——Roman to Integer(s

【编程之美】目录

第1章  游戏之乐——游戏中碰到的题目 1.1 让CPU占用率听你的指挥 1.2 中国象棋将帅问题 1.3 一摞烙饼的排序 1.4 买书问题 第2章  数字之魅——数字中的技巧 2.1 求二进制中1的个数 2.2 不要被阶乘吓倒 2.3 寻找发帖"水王" 2.4 1的数目 2.5 寻找最大的K个数 2.6 精确表达浮点数 2.7 最大公约数问题 2.8 找符合条件的整数 2.9 斐波那契(Fibonacci)数列 2.10 寻找数组中的最大值和最小值 2.11 寻找最近点对 2.12

编程之美-翻烙饼Java实现

从今天开始每天至少一个算法. 前言 翻烙饼问题是非常经典的问题,星期五的晚上,一帮同事在希格玛大厦附近的"硬盘酒吧"多喝了几杯.程序员多喝了几杯之后谈什么呢?自然是算法问题.有个同事说: "我以前在餐馆打工,顾客经常点非常多的烙饼.店里的饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好--小的在上面,大的在下面.由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了. 我后来想,这实际上是个有趣的排序问