C++_学习随笔_牛郎织女迷宫

先上题目,

 

 

 

 

这个题...我用C++写了200+行的代码,不包括数据纠错(比如输入点超过了迷宫的限制,或者输入数据时输入的不是整数)

所以记录一下。

C++ vector自带的方法比较少,而且感觉不是很好用,几乎所有用到的方法都是重新写的。

 

我的思路是,已知牛郎的坐标(x1,y1) 然后织女的坐标(x2,y2)

那么无论牛郎怎么走,一定要走m=|x1-x2|个横坐标,n=|y1-y2|个纵坐标(先不考虑封闭房间)

并且路线一定是C(m+n,n)条(排列组合)。

我使用0 表示牛郎在x轴上走了一步,用1 表示牛郎在y轴上走了一步。

这样就能表示一条路径,比如牛郎在上图中的a点,织女在b点,那么 000111 就表示牛郎先向右走了3步,然后向下走了3

当我们知道了两人的坐标后,也就知道了0的个数和1的个数,以上图为例,我们要求的路径就是 000111 这六个数字的无重复全排列。

这样我们就知道了所有牛郎走向织女的可能路径。接下来就要排除被封闭房间所挡住的路径。

以同样的方法,我们求出所有 牛郎通向封闭房间(在牛郎和织女之间的封闭房间)的路径,然后再删除 牛郎通向织女路径中 前部分与之重合的路径。

以上图为例,我们找到了一条通向封闭房间的路径,0011接下来删除所有以 0011 开头的 牛郎通向织女的路径,然后重复这一过程,直至所有经过该封闭房间的路径都被删除,

再以同样的方法检索剩余的封闭房间。

最终剩下的,就是所有不经过封闭房间的 牛郎通向织女的路径。

 

另,在求全排列这个地方卡住了好久,最终找到一种巧妙的算法并改了改。

大概就是,先排出最小的路径,比如000111,然后从该数串的最后开始 ,选取两个数字进行比较,直到找到前一个数字比后一个数字小,在目前这个只有0 和1模型中就是找到“01”,

然后将左边这个0与最靠后的1进行交换,比如001110,然后把原来0位置后面的所有元素翻转,最终变成001011。这就是仅大于000111的排列,按照上述方法不断重复,最终就能按照从小到大的顺序找全所有的 排列。

我可能说的不太明白。我参考的是https://blog.csdn.net/morewindows/article/details/7370155中的非递归全排列方法。

贴上代码:

  1 #include<iostream>
  2 #include<vector>
  3 #include<string>
  4 using namespace std;
  5 
  6 int min(int a, int b) {
  7     if (a < b)
  8         return a;
  9     else
 10         return b;
 11 }
 12 
 13 int max(int a, int b) {
 14     if (a > b)
 15         return a;
 16     else
 17         return b;
 18 }
 19 
 20 void Swap(int* a, int* b) {
 21     int temp;
 22     temp = *a;
 23     *a = *b;
 24     *b = temp;
 25 }
 26 
 27 void deleteVector(vector<vector<int>>& v, int i) {
 28     //删除v中第i条路径
 29     vector<int>* x, * y, temp;
 30     x = &v[i];
 31     y = x + 1;
 32     for (int n = 0; n < v.size() - i - 1; n++) {
 33         temp = *x;
 34         *x = *y;
 35         *y = temp;
 36         x++; y++;
 37     }
 38     v.pop_back();
 39 
 40 }
 41 
 42 void showVector(vector<int>v) {
 43     //打印一条路径
 44     for (int i = 0; i < v.size(); i++) {
 45         cout << v[i] << ",";
 46     }
 47     cout << endl;
 48 }
 49 
 50 void showVector(vector<vector<int>> v) {
 51     //打印所有路径
 52     for (int i = 0; i < v.size(); i++) {
 53         cout << "" << i + 1 << "条路线:";
 54         for (int j = 0; j < v[0].size(); j++) {
 55             cout << v[i][j] << ",";
 56         }
 57         cout << endl;
 58     }
 59 }
 60 
 61 void TurnLanguage(vector<vector<int>> v,string x,string y) {
 62     //转换成自然语言的路径
 63     for (int i = 0; i < v.size(); i++) {
 64         cout << "" << i + 1 << "条路线为: 从起始点开始-";
 65         for (int j = 0; j < v[0].size(); j++) {
 66             if(v[i][j]==0)
 67                 cout << x << "-";
 68             else
 69                 cout << y << "-";
 70         }
 71         cout << endl;
 72     }
 73 }
 74 
 75 void reverseVector(int* a, int* b) {
 76     //翻转 vector,vector自带的reverse老报错,没看出所以然来,所以重新写了个
 77     while (a < b) {
 78         Swap(a++, b--);
 79     }
 80 }
 81 
 82 void all_path(vector<int> first_path, vector<vector<int>>& allthepath) {
 83     //通过第一条路径 推算出所有路径
 84     bool hasnext = true;
 85     int* p, *q,*m,*the_end,counter=1;
 86     the_end = &first_path[first_path.size() - 1];
 87     m = &first_path[first_path.size() - 1];
 88     p = &first_path[first_path.size() - 1];
 89     q = &first_path[first_path.size() - 2];
 90 
 91     allthepath.push_back(first_path);
 92 
 93     while (hasnext) {
 94         if (*q >= *p) {
 95             //不触发
 96             q--; p--;
 97             if (p == &first_path[0]) {
 98                 hasnext = false;
 99                 break;
100             }
101         }
102         else {
103             //递增出现,触发,准备交换
104             counter++;
105             while (*m != 1) {
106                 m--;
107             }
108             Swap(m, q);
109             reverseVector(p, the_end);
110             //cout << "find "<<counter<<" path!:          " ;
111             //showVector(first_path);
112             the_end = &first_path[first_path.size() - 1];
113             m = &first_path[first_path.size() - 1];
114             p = &first_path[first_path.size() - 1];
115             q = &first_path[first_path.size() - 2];
116             int reverse_counter = first_path.size() - 1;
117             allthepath.push_back(first_path);
118         }
119 
120     }
121 }
122 
123 int main() {
124     int row, column;
125     int x_m, y_m, x_f, y_f;
126     int boxes;
127     int steps_x, steps_y;
128     vector<int> boxes_x,boxes_y;
129     
130     cout << "Please input the row and column:" << endl;
131     cin >> row>>column;
132     cout << "Please input niulang'x_ord and y_ord:" << endl;
133     cin >> x_m >> y_m;
134     cout << "Please input zhinv'x_ord and y_ord:" << endl;
135     cin >> x_f >> y_f;
136     cout << "How many boxes do you want?";
137     cin >> boxes;
138     for (int i = 0; i < boxes; i++) {
139         int temp_x, temp_y;
140         cout << "Please input the x_ord and y_ord of " << i +1<< " box" << endl;
141         cin >> temp_x >> temp_y;
142         boxes_x.push_back(temp_x);
143         boxes_y.push_back(temp_y);
144     }
145     steps_x = abs(x_m - x_f);
146     steps_y = abs(y_m - y_f);
147     //创建一个最小的数 作为第一条路径
148     vector<int> thefirst_path;
149     vector<vector<int>> solution;
150     for (int i = 0; i < steps_x; i++) {
151         thefirst_path.push_back(0);
152     }
153     for (int j = 0; j < steps_y; j++) {
154         thefirst_path.push_back(1);
155     }
156     all_path(thefirst_path,solution);
157     showVector(solution);
158     //删除路线中被盒子挡住的
159     for (int i = 0; i < boxes; i++) {
160          //筛除不在路径上的盒子
161         if (boxes_x[i]<=max(x_f,x_m)&&boxes_x[i]>=min(x_f,x_m)&&boxes_y[i]>=min(y_m,y_f)&&boxes_y[i]<=max(y_m,y_f)) {
162             //记录所有牛郎通向盒子的路径
163             int x_distance, y_distance;
164             vector<int> firstpath_NiuAndBox;
165             vector<vector<int>> paths_NiuAndBox;
166             x_distance = abs(x_m - boxes_x[i]);
167             y_distance = abs(y_m - boxes_y[i]);
168             for (int j = 0; j < x_distance;j++) {
169                 firstpath_NiuAndBox.push_back(0);
170             }
171             for (int j = 0; j < y_distance; j++) {
172                 firstpath_NiuAndBox.push_back(1);
173             }
174             all_path(firstpath_NiuAndBox,paths_NiuAndBox);
175             cout << "-----------"<<endl;
176             showVector(paths_NiuAndBox);
177             //删除牛郎通向织女的 且通过该盒子的路径
178             int same_counter=0;
179             for (int k = 0; k < paths_NiuAndBox.size(); k++) {
180                 //先处理牛郎通向盒子的第k条路径
181                 for (int u = 0; u < solution.size();u++) {
182                     //与牛郎通向织女的第u条路径比较
183                     same_counter = 0;
184                     for (int v = 0; v < paths_NiuAndBox[0].size();v++) {
185                         //通向盒子的 第k,v个元素与通向织女的第u,v个元素进行比较,如果全中,则删除该条牛郎通向织女的路径
186                         if (paths_NiuAndBox[k][v] == solution[u][v]) {
187                             same_counter++;
188                         }
189                         if (same_counter  == paths_NiuAndBox[0].size()) {
190                             //全中,删除且u--
191                             
192                             deleteVector(solution,u);
193                             u--;
194                         }
195                     }
196                 }
197             }
198 
199         }
200         
201     }
202     cout << "---------------------" << endl;
203     showVector(solution);
204     cout << "---------------------" << endl;
205     //转换成自然语言
206     cout << "所以,可行方案如下:" << endl;
207     string vector_x,vector_y;
208     if (x_m > x_f)
209         vector_x = "";
210     else
211         vector_x = "";
212     if (y_m > y_f)
213         vector_y = "";
214     else
215         vector_y = "";
216 
217     TurnLanguage(solution, vector_x, vector_y);
218 }

 

 

同时 输入为 (5,5) 行列 (这个行列我都没用到...)

(4,1) 牛郎坐标

(1,4) 织女坐标

  1            封闭房间个数

(2,3) 封闭房间坐标

的结果:

 

 

 

第一块表示所有不考虑封闭房间的牛郎织女路径

第二块表示牛郎到封闭房间的路径

第三块表示 第一块去掉所有第二块开头的,也就是不经过该封闭房间的路径。

最终转换为自然语言。

 

感觉应该有更巧妙的方法,希望有大佬指正,我这代码也太多了。

 

posted @ 2020-04-18 02:34  vantablack  阅读(346)  评论(0编辑  收藏  举报