1. 程式人生 > >程式設計之美——一摞烙餅的排序(暴搜+剪枝)

程式設計之美——一摞烙餅的排序(暴搜+剪枝)

題目

分析

深度優先搜尋遍歷每一種情況,去翻轉次數最小的,當然,還要加一些剪枝,畢竟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