北京东道设计公司官网seo流量优化
今天进入搜索的学习。搜索问题一般较为复杂,状态空间很大,使用规划过的遍历/枚举,不重复或不遗漏的找到解决问题。有广度优先搜索以及深度优先搜索。广度优先搜索访问某节点时,将其邻居置为待访问节点。广度优先遍历一般用于寻找最优解。层序遍历本质上就是广度优先遍历,BFS的核心数据结构是队列。深度优先遍历是一条路走到黑,实现DFS就是从大问题到小问题,知道最小问题,用递归或分治法实现。
第一题是树的高度。把这棵树看成图的特例,从根出发使用BFS遍历。
#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
using namespace std;
int main(){int n,m;scanf("%d%d", &n,&m);//编号是1,2,3....nvector<vector<int>> tree(n+1);//tree[0]没用//tree[1]~tree[n] 对应不同编号的节点for(int i = 0;i<n-1;i++){int u, v;scanf("%d%d", &u, &v);tree[u].push_back(v);tree[v].push_back(u);}queue<int> Tovisit;//用来做BFS的队列vector<int> distance(n+1);//用来判断某个节点是否访问过//记录根节点到这个节点的最短距离for(int i = 1;i<=n;i++){distance[i] = -1;}distance[m] = 0;//根节点到根节点的距离是0int maxdis = 0;Tovisit.push(m);while(!Tovisit.empty()){int cur = Tovisit.front();Tovisit.pop();for(int i = 0;i<tree[cur].size();i++){int child = tree[cur][i];if(distance[child]!=-1){continue;}Tovisit.push(child);distance[child] = distance[cur]+1;maxdis = distance[child];}}printf("%d", maxdis);return 0;
}
第二题是玛雅人的密码。使用map结构避免重复。
#include <stdio.h>
#include <string>
#include <queue>
#include <unordered_map>//时间复杂度O(1)
using namespace std;
int main(){char strarr[20] = {0};int n;scanf("%d", &n);scanf("%s", strarr);if(n < 4) {printf("-1");return 0;}string str = strarr;queue<string> myqueue;myqueue.push(str);unordered_map<string , int> dismap;dismap.insert({str, 0});while(!myqueue.empty()){string cur = myqueue.front();if(cur.find("2012")!=string::npos){printf("%d\n", dismap[cur]);break;}myqueue.pop();for(int i = 0;i<cur.size()-1;i++){string next = cur;char temp = next[i];next[i] = next[i+1];next[i+1] = temp;if(dismap.count(next) == 0){myqueue.push(next);dismap.insert({next, dismap[cur]+1});}}}if(myqueue.empty()) printf("-1\n");
}
第三题是走路还是走公交,使用map指示当前pos以及到达pos的最小时间。太多map.count导致超时了。
#include <stdio.h>
#include <queue>
#include <unordered_map>
using namespace std;
int main(){int n,k;while(scanf("%d%d", &n, &k)!= EOF){queue<int> pos;//位置队列pos.push(n);unordered_map<int, int> res;//位置 时间res.insert({n, 0});while(!pos.empty()){int curpos = pos.front();//当前位置pos.pop();if(curpos == k){printf("%d\n", res[curpos]);break;}else{if(res.count(curpos+1) == 0&&(curpos+1)<=100000&&curpos<k){res.insert({curpos+1, res[curpos]+1});pos.push(curpos+1);}if(res.count(curpos-1) == 0&&(curpos-1)>=0){res.insert({curpos-1, res[curpos]+1});pos.push(curpos-1);}if(res.count(curpos*2) == 0&&curpos*2<=100000&&curpos<k){res.insert({curpos*2, res[curpos]+1});pos.push(curpos*2);}}}}return 0;
}
#include <stdio.h>
#include <queue>
#include <unordered_map>
using namespace std;const int MAX = 100000; // 我们可以尝试用数组去替代unordered_map
bool visited[MAX + 1]; // 使用数组来记录位置是否被访问过,下标表示位置,值表示是否访问,初始化为false
int steps[MAX + 1]; // 使用数组来记录到达每个位置的步数,初始化为0int main() {int n, k;while (scanf("%d%d", &n, &k) != EOF) {queue<int> pos;pos.push(n);visited[n] = true; // 标记起始位置已访问steps[n] = 0; // 起始位置步数为0while (!pos.empty()) {int curpos = pos.front();pos.pop();if (curpos == k) {printf("%d\n", steps[curpos]);break;}else {// 当前位置+1是否在范围内且未被访问过if (curpos + 1 <= MAX &&!visited[curpos + 1]) { visited[curpos + 1] = true;steps[curpos + 1] = steps[curpos] + 1;pos.push(curpos + 1);}// 当前位置-1是否在范围内且未被访问过if (curpos - 1 >= 0 &&!visited[curpos - 1]) { visited[curpos - 1] = true;steps[curpos - 1] = steps[curpos] + 1;pos.push(curpos - 1);}// 当前位置*2是否在范围内、未被访问过且乘2操作合理(避免超过目标位置且浪费计算)if (curpos * 2 <= MAX &&!visited[curpos * 2] && curpos <= k) { visited[curpos * 2] = true;steps[curpos * 2] = steps[curpos] + 1;pos.push(curpos * 2);}}}// 重置数组状态for (int i = 0; i <= MAX; ++i) { visited[i] = false;steps[i] = 0;}}return 0;
}
第四题是逃离迷宫。给了,这题是真不会。
#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <limits.h>#include <queue>using namespace std;struct Node {int x;int y;int direct; // 1,2,3,4 四个方向int turn; // 已经转弯的次数Node(int _x, int _y, int _direct, int _turn) {x = _x;y = _y;direct = _direct;turn = _turn; }};bool operator<(Node lsh, Node rsh) {return lsh.turn > rsh.turn;}char maze[200][200];int main() {int t;scanf("%d", &t);for (int idx = 0; idx < t; ++idx) {int m, n;scanf("%d%d", &m, &n);// m n 是行和列for (int i = 0; i < m; ++i) {scanf("%s", maze[i]);}int k, x1, x2, y1, y2;// k 最多转弯数 scanf("%d%d%d%d%d", &k, &y1, &x1, &y2, &x2);--x1; // 将编号从1到N 转成下标从0到N-1--x2;--y1;--y2;int vis[101][101][5];for (int i = 0; i < 101; ++i) {for (int j = 0; j < 101; ++j) {vis[i][j][1] = INT_MAX; //目前按照1方向到达i,j位置所经历的最小转弯次数vis[i][j][2] = INT_MAX; //目前按照2方向到达i,j位置所经历的最小转弯次数vis[i][j][3] = INT_MAX; //目前按照3方向到达i,j位置所经历的最小转弯次数vis[i][j][4] = INT_MAX; //目前按照4方向到达i,j位置所经历的最小转弯次数}}priority_queue<Node> tovisit; // 使用优先队列保存待访问的邻居结点Node root(x1, y1, 0, 0); // 0 表示当前没有任何方向tovisit.push(root);bool isfind = false;while (!tovisit.empty()) {// 当前结点Node cur = tovisit.top();int x = cur.x, y = cur.y;tovisit.pop();if (cur.x == x2 && cur.y == y2) {isfind = true;break;}// 假设下一步走1方向int nextTurn = (cur.direct == 1) ? cur.turn : cur.turn + 1;// 假设下一个位置在范围内,且消耗转向次数更少,则入队if (nextTurn <= k + 1 && x + 1 < m && maze[x + 1][y] != '*' && vis[x + 1][y][1] > nextTurn) {Node next(x + 1, y, 1, nextTurn);tovisit.push(next);vis[x + 1][y][1] = nextTurn;}// 假设下一步走2方向nextTurn = (cur.direct == 2) ? cur.turn : cur.turn + 1;if (nextTurn <= k + 1 && x - 1 >= 0 && maze[x - 1][y] != '*' && vis[x - 1][y][2] > nextTurn) {Node next(x - 1, y, 2, nextTurn);tovisit.push(next);vis[x - 1][y][2] = nextTurn;}nextTurn = (cur.direct == 3) ? cur.turn : cur.turn + 1;if (nextTurn <= k + 1 && y + 1 < n && maze[x][y + 1] != '*' && vis[x][y + 1][3] > nextTurn) {Node next(x, y + 1, 3, nextTurn);tovisit.push(next);vis[x][y + 1][3] = nextTurn;}nextTurn = (cur.direct == 4) ? cur.turn : cur.turn + 1;if (nextTurn <= k + 1 && y - 1 >= 0 && maze[x][y - 1] != '*' && vis[x][y - 1][4] > nextTurn) {Node next(x, y - 1, 4, nextTurn);tovisit.push(next);vis[x][y - 1][4] = nextTurn;}}if (isfind) {printf("yes\n");// printf("%d\n", dis[x2][y2]);}else {printf("no\n");}}return 0;}
第五题是八皇后。经典递归牢题。
#include <stdio.h>
#include <vector>
using namespace std;
//希望所有的函数都能访问queenvec
vector<vector<int>> queenvec;//所有的合法皇后序列
void DFSFindQueen(vector<int> &queen, int pos){//打算放下一个皇后for(int i = 1;i <= 8;i++){//i就是第pos号皇后打算放的列数bool isOK = true;for(int j = 0; j<pos;j++){//j用来遍历之前已经放好的皇后0~pos-1if(queen[j] == i||//第j号皇后与pos号皇后在同一列pos - j == queen[j]-i||pos - j == i - queen[j]//在一条斜对角线上){isOK = false;break;}if(isOK == true){//将pos号皇后放置好queen.push_back(i);if(pos == 7){//八个皇后都已经放好了queenvec.push_back(queen);}else {DFSFindQueen(queen, pos+1);}queen.pop_back();//为下一种i的可能性做准备}}}
}
int main(){vector<int> queen;//用来记录已经放好的皇后的位置DFSFindQueen(queen, 0);return 0;
}
取巧的方法:如果解的数量有限,而且算解的时间很长。可以先写一个比较差的算法先生成所有的解,保存所有的解,写入代码,访问时直接访问解集合即可。著名的打表法。
#include <stdio.h>
#include <vector>
using namespace std;
//希望所有的函数都能访问queenvec
vector<vector<int>> queenvec;//所有的合法皇后序列
void DFSFindQueen(vector<int> &queen, int pos){//打算放下一个皇后for(int i = 1;i <= 8;i++){//i就是第pos号皇后打算放的列数bool isOK = true;for(int j = 0; j<pos;j++){//j用来遍历之前已经放好的皇后0~pos-1if(queen[j] == i||//第j号皇后与pos号皇后在同一列pos - j == queen[j]-i||pos - j == i - queen[j]//在一条斜对角线上){isOK = false;break;}}if(isOK == true){//将pos号皇后放置好queen.push_back(i);if(pos == 7){//八个皇后都已经放好了queenvec.push_back(queen);// printf("\"");// for(int k = 0; k<8;k++){// printf("%d", queen[k]);// }// printf("\",\n");}else {DFSFindQueen(queen, pos+1);}queen.pop_back();//为下一种i的可能性做准备}}
}
int main(){vector<int> queen;//用来记录已经放好的皇后的位置DFSFindQueen(queen, 0);int n;scanf("%d", &n);for(int i = 0; i<n;i++){int b;scanf("%d", &b);for(int j = 0; j < queenvec[b-1].size();j++){printf("%d", queenvec[b-1][j]);}printf("\n");}return 0;
}
第六题是数组划分。直接超时被拿下。
#include <vector>
#include <stdio.h>
using namespace std;
int sum = 0;//记录数组的和
int diff = 0;//记录遍历过程中最小的差值
void DFSFindMinDiff(vector<int> &arr, int pos, int sa){if(pos == arr.size()){return;}//arr[pos]不放入集合中DFSFindMinDiff(arr, pos+1,sa);//arr[pos]放入集合中int newdiff;//记录当前的差值if(2*(sa+arr[pos]) > sum){newdiff = 2*(sa+arr[pos])-sum;}else newdiff = sum - 2*(sa+arr[pos]);if(newdiff < diff){diff = newdiff;}DFSFindMinDiff(arr, pos+1, sa+arr[pos]);
}
int main(){vector<int> arr;int i;while(scanf("%d", &i)!=EOF){arr.push_back(i);}for(int i = 0; i<arr.size();i++){sum += arr[i];}diff = sum;DFSFindMinDiff(arr, 0, 0);//sa+sb = sum//sb-sa = diffint sa = (sum+diff)/2;int sb = (sum - diff)/2;printf("%d%d", sa, sb);}
对代码进行剪枝,去掉不必要的搜索过程,尽快找到最优解,提前终止搜索。可以排序arr,先执行加入arr[pos],再执行不加入。一开始sa<sb,中间某个时刻sb大,此时没有必要继续。若diff为0或为1,终止,其次arr[pos]>sum/2。
#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <vector>#include <algorithm>using namespace std;int sum = 0; //记录数组的和int diff = 0; // 记录遍历过程最小的差值bool exitFlag = false; // 记录是否要提前退出void DFSFindMinDiff(vector<int> &arr, int pos, int sa) {if (pos == arr.size() || exitFlag == true) {return;}// arr[pos] 放入到 a集合当中int newdiff; //记录当前的差值if (2 * (sa + arr[pos]) - sum > 0) {newdiff = 2 * (sa + arr[pos]) - sum;}else {newdiff = sum - 2 * (sa + arr[pos]);}if (newdiff < diff) {diff = newdiff;if (diff == 0 || diff == 1 || 2 * arr[pos] > sum) {exitFlag = true;}}if (2 * (sa + arr[pos]) - sum < 0) {DFSFindMinDiff(arr, pos + 1, sa + arr[pos]);}// arr[pos] 不放入 a集合中DFSFindMinDiff(arr, pos + 1, sa);}bool compare(int lhs, int rhs) {return lhs > rhs;}int main() {vector<int> arr;int i;while (scanf("%d", &i) != EOF) {arr.push_back(i);}for (int i = 0; i < arr.size(); ++i) {sum += arr[i];}diff = sum;sort(arr.begin(), arr.end(), compare);DFSFindMinDiff(arr, 0, 0);// sa+sb = sum// sb-sa = diffint sa = (sum - diff) / 2;int sb = sa + diff;printf("%d %d\n", sb, sa);return 0;}
兄弟们轻易别跨考,要补的东西太多了,博主现在天天都在焦虑。祝刷到的兄弟们一切顺利吧,能考上出个帖子给大伙讲讲备考过程。