北京大学程序设计实习2017年期末考试自解
1:蜜蜂
一开始想用广搜,结果递归算法爆TLE了,这时候不得不抱dp的大腿,记忆化就是香。

#include<iostream> #include<cstring> using namespace std; long long int way[51]; int main() { int T; cin >> T; while (T--) { int a, b; cin >> a >> b; memset(way, 0, sizeof(way)); way[a + 1] = 1; way[a + 2] = 2; if (b <= a + 2) { cout << 1 << endl; continue; } for (int i = a+3; i <= b; i++) { way[i] = way[i - 2] + way[i - 1]; } cout << way[b] << endl; } return 0; }
2:要变多少次
想了半天字符串怎么操作,卡在字符串读取的终点怎么判定上,结果发现有个好东西叫strlen。
看了大佬的代码,两点值得我学习,一是将字符串转为int数组,二是暴力破解的过程,高效直接。

#include<iostream> #include<cstring> using namespace std; char str[1001]; int s[1001]; int l, num; void oper() { int sum; for (int i = 0; i <= l; i++)//i是第一个1出现的地方 { sum = 0; for (int j = 0; j < i; j++) if (s[j] == 1)sum++; for (int j = i; j < l; j++) if (s[j] == 0)sum++; if (num > sum) num = sum;//暴力破解 } } int main() { int T; cin >> T; while (T--) { cin >> str;//正序存储在str里面 l = strlen(str); for (int i = 0; i < l; i++) s[i] = str[i] - '0'; num = 1000; oper(); cout << num << endl; } return 0; }
3:未名冰场
典型的深搜题,刚练过的城堡问题马上就用上了。

#include<iostream> #include<cstring> using namespace std; int visited[101][101];//判定是否已访问过 char map[101][101];//湖面 int icePlaceNum;//结冰区域的数量 int n, m;//map的总行数和列数 void DFS(int x, int y)//对当前冰块进行深搜遍历 { if (visited[x][y])return;//之前访问过该冰块 visited[x][y] = 1; if (map[x][y + 1] == '@')DFS(x, y + 1); if (map[x][y - 1] == '@')DFS(x, y - 1); if (map[x + 1][y + 1] == '@')DFS(x + 1, y + 1); if (map[x + 1][y - 1] == '@')DFS(x + 1, y - 1); if (map[x - 1][y + 1] == '@')DFS(x - 1, y + 1); if (map[x - 1][y - 1] == '@')DFS(x - 1, y - 1); if (map[x + 1][y] == '@')DFS(x + 1, y); if (map[x - 1][y] == '@')DFS(x - 1, y); } int main() { int n, m; cin >> n >> m; while (n != 0 && m != 0) { for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) cin >> map[i][j]; icePlaceNum = 0;//结冰区域数清零 memset(visited, 0, sizeof(visited));//visited数组清零 for(int i=0;i<n;i++) for (int j = 0; j < m; j++) { if (!visited[i][j] && map[i][j]=='@')//冰块没有被访问过 { icePlaceNum++; DFS(i, j); } } cout << icePlaceNum << endl; cin >> n >> m; } return 0; }
4:迷阵
不得不说这道题出得让我一点都不想帮主角过关...写了一个小时还是WA了,样例过了,应该是有些情况没考虑到。

#include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; int m, n, H; int minStep = 1 << 30; char map[260][260]; int visited[260][260]; struct Node { int x, y;//坐标 int h;//剩余血量 int steps;//从起点到达该点所需步数 Node(int xx, int yy, int step, int hh) :x(xx), y(yy), steps(step), h(hh){} }; queue<Node> q;//构造队列 int dx[4] = { 0,0,1,-1 }; int dy[4] = { 1,-1,0,0 }; bool check(Node tmp, int i) { if ((tmp.x + dx[i]) >= 0 && (tmp.x + dx[i]) < m && (tmp.y + dy[i]) >= 0 && (tmp.y + dy[i]) < n)//坐标合法 { if ((map[tmp.x + dx[i]][tmp.y + dy[i]] == '*' && tmp.h <= 1) || map[tmp.x + dx[i]][tmp.y + dy[i]] == '#' || visited[tmp.x + dx[i]][tmp.y + dy[i]]) return false; return true; } return false; } void bfs()//广搜 { Node start(0, 0, 0, H);//初始化起始点 visited[0][0] = 1; q.push(start); while (!q.empty())//广搜遍历地图 { Node tmp = q.front();//读取队头元素 q.pop(); if (tmp.x == (m-1) && tmp.y == (n-1)) minStep = min(minStep, tmp.steps); for(int i = 0;i < 4; i++) { if (check(tmp, i))//检查下一点的坐标是否合法 { Node node(tmp.x + dx[i], tmp.y + dy[i], tmp.steps + 1, tmp.h); if (map[tmp.x + dx[i]][tmp.y + dy[i]] == '*') node.h--; q.push(node); visited[tmp.x + dx[i]][tmp.y + dy[i]] = 1; } } } } int main() { int T; cin >> T; while (T--) { memset(visited, 0, sizeof(visited)); minStep = 1 << 30;//重置最小步数 cin >> m >> n >> H;//读入矩阵行列和血量 for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) cin >> map[i][j]; bfs(); cout << minStep << endl; } return 0; }
又码了一遍大佬的代码,学习学习。不得不说大佬的代码太简洁了,整体思想和我的一致,但是简洁优美orz。

#include<iostream> #include<cstring> #include<queue> using namespace std; int m, n, H; char s[260][260]; int visit[260][260]; int dx[4] = { 0,0,1,-1 }; int dy[4] = { 1,-1,0,0 }; struct nodes { int x, y; int h; int T; }; bool check(int a , int b) { if (a < 0 || a >= m || b < 0 || b >= n)return false; if (s[a][b] == '#') return false; return true; } int main() { int T; cin >> T; while (T--) { queue<nodes> q; memset(visit, 0, sizeof(visit)); memset(s, 0, sizeof(s)); int flag = 0; cin >> m >> n >> H; for (int i = 0; i < m; i++)cin >> s[i]; nodes node0; node0.x = 0; node0.y = 0; node0.h = H; node0.T = 0; visit[0][0] = H - 1; q.push(node0); while (!q.empty()) { nodes tmp = q.front(); if (tmp.h <= visit[tmp.x][tmp.y])//执行了visited的功能,因为步数多的到达同一点没有更多的血量,和步数少的比就没有任何优势了 { q.pop(); continue; } visit[tmp.x][tmp.y] = tmp.h; for (int i = 0; i < 4; i++) { int X = tmp.x + dx[i]; int Y = tmp.y + dy[i]; if (check(X, Y)) { if (X == m - 1 && Y == n - 1) { cout << tmp.T + 1 << endl; flag = 1; break; } nodes node; node.h = tmp.h; if (s[X][Y] == '*')node.h--; if (node.h == 0)continue;//不入队列 node.x = X; node.y = Y; node.T = tmp.T + 1; q.push(node); } } if (flag)break; q.pop(); } } return 0; }