kuangbin专题一:简单搜索
前:kuangbin的题目比较多,为了简化篇幅,对于一些常规的题目,就不再多写题干了,还望周知。
思路:dfs
#include <iostream> using namespace std; const int maxn = 15; int n, k; char mm[maxn][maxn]; bool vis[maxn]; int dfs(int line, int dep){ if(dep == k+1) return 1; int ans = 0; for(int i = line; i <= n; i++) for(int j = 1; j <= n; j++) if(mm[i][j]=='#' and !vis[j]){ vis[j] = true; ans += dfs(i+1, dep+1); vis[j] = false; } return ans; } int main(){ while(~scanf("%d%d", &n, &k) and (n!=-1 and k!=-1)){ for(int i = 1; i <= n; i++) scanf("%s", &mm[i][1]); int ans = dfs(1, 1); cout << ans << endl; } return 0; }
思路:bfs
#include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 33; int L, R, C; char mm[maxn][maxn][maxn]; bool vis[maxn][maxn][maxn]; int st[3], ed[3]; int dx[]={1,-1, 0, 0, 0, 0}, dy[]={0,0,1,-1,0,0}, dz[]={0,0,0,0,1,-1}; struct Pos{ int x, y, z, dis; }; int bfs(){ queue<Pos> q; Pos stpos = {st[0], st[1], st[2], 0}; q.push(stpos); vis[st[0]][st[1]][st[2]] = true; while(q.size()){ Pos cur = q.front(); q.pop(); int ux = cur.x, uy = cur.y, uz = cur.z; if(ux==ed[0] and uy==ed[1] and uz==ed[2]) return cur.dis; for(int i = 0; i < 6; i++){ int vx = ux+dx[i], vy = uy+dy[i], vz = uz+dz[i]; if(vx>0 and vx<=L and vy>0 and vy<=R and vz>0 and vz<=C){ if(!vis[vx][vy][vz] and mm[vx][vy][vz] != '#'){ vis[vx][vy][vz] = true; Pos tmppos = {vx, vy, vz, cur.dis+1}; q.push(tmppos); } } } } return -1; } int main(){ while(~scanf("%d%d%d", &L, &R, &C) and (L!=0 and R!=0 and C!=0)){ memset(vis, 0, sizeof(vis)); for(int i = 1; i <= L; i++) for(int j = 1; j <= R; j++) for(int k = 1; k <= C; k++){ cin >> mm[i][j][k]; if(mm[i][j][k] == 'S') {st[0] = i, st[1] = j, st[2] = k;} if(mm[i][j][k] == 'E') {ed[0] = i, ed[1] = j, ed[2] = k;} } int ans = bfs(); if(ans == -1) cout << "Trapped!" << endl; else cout << "Escaped in " << ans << " minute(s)." << endl; } return 0; }
思路:bfs,可以有特判,但没必要
#include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 1e5+5; int N, K; bool vis[maxn]; int dist[maxn]; int bfs(){ queue<int> q; q.push(N); vis[N] = true; while(q.size()){ int cur = q.front(); q.pop(); if(cur == K) return dist[K]; if(cur >= 1 and !vis[cur-1]){ vis[cur-1] = true; dist[cur-1] = dist[cur] + 1; q.push(cur-1); } if(cur <= K and !vis[cur+1]){ vis[cur+1] = true; dist[cur+1] = dist[cur] + 1; q.push(cur+1); } if(cur < maxn/2 and !vis[cur<<1]){ vis[cur<<1] = true; dist[cur<<1] = dist[cur] + 1; q.push(cur<<1); } } return -1; } int main(){ cin >> N >> K; cout << bfs() << endl; return 0; }
题意:有M×N的棋盘,棋盘每个格子为0或者1,每次可以选取一个格子,翻转该格子和其上下左右的四个格子的数字(0变成1,或者1变成0),求让整个棋盘全变成0需要最少的步骤。
思路:首先我们发现,要到达最终结果每个格子最多被翻一次,因为每次翻格子相当于对这个格子及周围格子做了一次异或操作,做两次就等于没有做任何操作。
也就是说我们要对整个棋盘做搜索,最多有215×15种情况,这显然是会超时的。注意到每行格子的颜色只有上下两行以及本行能够影响,所以可以剪枝去掉多余的情况。具体的,我们遍历完第一行翻转的所有情况,然后对于第一行的每一种情况,我们可以知道第二行哪些格子应该翻转,第三行也参照第二行的情况来决定,依此类推。
#include <iostream> #include <cstring> using namespace std; const int maxn = 16; const int INF = 0x3f3f3f3f; int N, M; int a[maxn][maxn], b[maxn][maxn], c[maxn][maxn]; int dx[]={1,-1,0,0, 0}, dy[]={0,0,1,-1, 0}; void fillLine(int line, int mask){ for(int i = 1; i <= N; i++) b[line][i] = (mask>>(N-i)) & 1; } bool isFlippable(int r, int c){ bool res = a[r][c]; for(int i = 0; i < 5; i++){ int nr = r + dx[i], nc = c + dy[i]; if(nr>=1 and nr<=M and nc>=1 and nc<=N) res ^= b[nr][nc]; } return res; } int solve(){ int step = 0; for(int i = 2; i <= M; i++) for(int j = 1; j <= N; j++) if(isFlippable(i-1, j)) // 如果上一行需要本行翻转 b[i][j] = 1; for(int i = 1; i <= N; i++){ // 检查最后一行是否满足条件 if(isFlippable(M, i)) return INF; } for(int i = 1; i <= M; i++) for(int j = 1; j <= N; j++) step += b[i][j]; return step; } void Print(){ for(int i = 1; i <= M; i++){ for(int j = 1; j <= N; j++) cout << c[i][j] << ' '; cout << endl; } cout << endl; } int main(){ int result = INF; cin >> M >> N; for(int i = 1; i <= M; i++) for(int j = 1; j <= N; j++) cin >> a[i][j]; for(int i = 0; i < (1<<N); i++){ memset(b, 0, sizeof(b)); fillLine(1, i); // 填充第一行 int totStep = solve(); if(totStep < result){ result = totStep; memcpy(c, b, sizeof(b)); } } if(result == INF) cout << "IMPOSSIBLE" << endl; else Print(); return 0; }
题意:给一个正整数n,找到任意一个n的非零倍数m,使得m的所有数字都是0或者1,m的位数不超过100位。
思路:这题比较坑的地方在于它的误导性,结果根本不需要100位,20位就足够了。对不超过20位的m的每个数位进行0,1遍历,如果恰好是n的倍数,那么输出该结果。尝试了使用bfs,发现会tle,但是dfs却ac了。。。
// dfs #include <iostream> #include <cstring> using namespace std; const int maxn = 16; const int INF = 0x3f3f3f3f; int n; bool found; void dfs(int pos, long long r){ if(pos >= 20 || found) return; if(r % n == 0){ found = true; cout << r << endl; return; } dfs(pos + 1, r * 10); dfs(pos + 1, r * 10 + 1); } int main(){ while(cin >> n and n){ found = false; dfs(1, 1); } return 0; }
思路:bfs。自己写的bfs一直wa,有点崩溃,这里贴上以前不知道在哪抄的代码。
#include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<limits.h> using namespace std; const int maxn = 10001; int a[maxn], vis[maxn]; int d[] = {1, 10, 100, 1000}; int main() { int n; cin >> n; for(int i = 2; i < maxn; i++) if(a[i] == 0) for(int j = i*i; j < maxn; j += i) a[j] = 1; while(n--) { memset(vis, -1, sizeof(vis)); int x, y; cin >> x >> y; queue<int> q; q.push(x); vis[x] = 0; while(!q.empty()) { int tmp = q.front(); q.pop(); if(tmp == y) { cout << vis[tmp] << endl; continue; } for(int i = 0; i < 4; i++) { for(int j = i==3?1:0; j <= 9; j++) { int nextn = tmp-((tmp/d[i]) % 10) * d[i] + j * d[i]; if(a[nextn]==0 && vis[nextn]==-1) { q.push(nextn); vis[nextn] = vis[tmp] + 1; } } } } } return 0; }
思路:简单遍历,但是提交后编译器一直在字符串比较时报错,(((φ(◎ロ◎;)φ))),还是用以前的代码好了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<queue> #include<map> using namespace std; int n, C; string ed, s1, s2; int f() { map<string, int> vis; string tem = s1 + s2; int step = 0; while(tem != ed && !vis[tem]) { vis[tem] = 1; step++; string newS = tem; int index = 0; for(int i = 0; i < C; i++) { newS[index++] = tem[i+C]; newS[index++] = tem[i]; } tem = newS; } if(tem == ed) return step; return -1; } int main() { cin >> n; for(int i = 1; i <= n; i++) { cin >> C; cin >> s1 >> s2 >> ed; cout << i << " " << f() << endl; } return 0; }
思路:bfs。这题一开始写了很大一串代码,翻了翻以前写的,发现直接将结果写入到一个字符串中更加的方便,给了我很大的启发。LJ编译器,string库问题好大,直接CE。以前代码+1,虽然写的有点蠢,不够优雅,将就着看吧。
#include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 105; int A, B, C; bool vis[maxn][maxn]; struct Pair{ int a, b, step; string op; Pair(int aa, int bb, int sstep, string oop = ""){ a = aa, b = bb, step = sstep; op = oop; } }; Pair Fill(Pair &p, string seq){ int a = seq[0] == '1' ? A : p.a; int b = seq[0] == '1' ? p.b : B; Pair res{a, b, p.step+1, p.op + "FILL("+seq+")\n"}; return res; } Pair Drop(Pair &p, string seq){ int a = seq[0] == '1' ? 0 : p.a; int b = seq[0] == '1' ? p.b : 0; Pair res{a, b, p.step+1, p.op + "DROP("+seq+")\n"}; return res; } Pair Pour(Pair &p, string sq1, string sq2){ int aa, bb; if(sq1[0] == '2'){ aa = min(A, p.a+p.b); bb = p.a+p.b - aa; } else{ bb = min(B, p.a+p.b); aa = p.a+p.b - bb; } Pair res{aa, bb, p.step+1, p.op + "POUR("+sq1+","+sq2+")\n"}; return res; } Pair bfs(){ queue<Pair> q; q.push({0, 0, 0}); vis[0][0] = true; while(q.size()){ Pair p = q.front(); q.pop(); if(p.a == C or p.b == C) return p; if(!vis[A][p.b]){ vis[A][p.b] = true; q.push(Fill(p, "1")); } if(!vis[p.a][B]){ vis[p.a][B] = true; q.push(Fill(p, "2")); } if(!vis[0][p.b]){ vis[0][p.b] = true; q.push(Drop(p, "1")); } if(!vis[p.a][0]){ vis[p.a][0] = true; q.push(Drop(p, "2")); } if(p.b > 0){ Pair np = Pour(p, "2", "1"); if(!vis[np.a][np.b]){ vis[np.a][np.b] = true; q.push(np); } } if(p.a > 0){ Pair np = Pour(p, "1", "2"); if(!vis[np.a][np.b]){ vis[np.a][np.b] = true; q.push(np); } } } Pair res{-1, -1, -1}; return res; } int main(){ cin >> A >> B >> C; Pair res = bfs(); if(res.step == -1){ cout << "impossible" << endl; return 0; } cout << res.step << endl; cout << res.op; return 0; }
思路:终于遇到不是POJ的题了。如果要分情况讨论就很麻烦,看到数据规模较小(地图长宽不超过10),我们可以直接遍历所有可能的起始点组合。这题给我的启发是能交给机器做的事,就不要交给人去做。
#include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 12; const int INF = 0x3f3f3f3f; int T, n, m; char mm[maxn][maxn]; bool vis[maxn][maxn]; struct Pos{ int x, y, step; }; int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1}; inline bool is_valide(int x, int y){ bool res = x>=1 && x<=n && y>=1 && y<=m; res = res && mm[x][y]=='#' && !vis[x][y]; return res; } int bfs(int x1, int y1, int x2, int y2){ int res = 0; queue<Pos> q; q.push((Pos){x1, y1, 0}); q.push((Pos){x2, y2, 0}); vis[x1][y1] = vis[x2][y2] = true; while(q.size()){ Pos p = q.front(); q.pop(); for(int i = 0; i < 4; i++){ int nx = p.x + dx[i], ny = p.y + dy[i]; if(is_valide(nx, ny)){ vis[nx][ny] = true; q.push((Pos){nx, ny, p.step+1}); res = max(res, p.step+1); } } } for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(mm[i][j]=='#' and !vis[i][j]) return INF; return res; } int main(){ cin >> T; for(int t = 1; t <= T; t++){ cin >> n >> m; for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) cin >> mm[i][j]; int res = INF; for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) for(int ii = 1; ii <= n; ii++) for(int jj = 1; jj <= m; jj++){ if(mm[i][j]=='#' and mm[ii][jj]=='#'){ memset(vis, false, sizeof(vis)); res = min(res, bfs(i, j, ii, jj)); } } cout << "Case " << t << ": "; if(res == INF) cout << -1 << endl; else cout << res << endl; } return 0; }
思路:bfs。可以让火和人每次分别走一步,也可以先求出火到达所有点的距离,再求人是否可以跑出去。这题的坑在于火可能不止一个。
#include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 1002; const int INF = 0x3f3f3f3f; int t, r, c; char mm[maxn][maxn]; bool vis[maxn][maxn]; struct Pos{ int x, y, step; bool j; // is joe here? }; int dx[]={1,-1,0,0}, dy[]={0,0,1,-1}; inline bool isValide(int x, int y){ bool res = x>=1 && x<=r && y>=1 && y<=c; res = res && mm[x][y]=='.' && !vis[x][y]; return res; } inline bool onBound(int x, int y){ return x==1 || x==r || y==1 || y==c; } queue<Pos> q; int bfs(){ while(q.size()){ Pos p = q.front(); q.pop(); if(onBound(p.x, p.y) and p.j){ return p.step; } for(int i = 0; i < 4; i++){ int nx = p.x + dx[i], ny = p.y + dy[i]; if(isValide(nx, ny)){ vis[nx][ny] = true; q.push({nx, ny, p.step+1, p.j}); } } } return INF; } int main(){ cin >> t; while(t--){ memset(vis, false, sizeof(vis)); while(q.size()) q.pop(); cin >> r >> c; for(int i = 1; i <= r; i++) for(int j = 1; j <= c; j++) cin >> mm[i][j]; for(int i = 1; i <= r; i++) for(int j = 1; j <= c; j++) if(mm[i][j] == 'F'){ Pos fire = {i, j, 0, false}; q.push(fire); vis[i][j] = true; } for(int i = 1; i <= r; i++) for(int j = 1; j <= c; j++) if(mm[i][j] == 'J'){ Pos joe = {i, j, 0, true}; q.push(joe); vis[i][j] = false; } int res = bfs(); if(res == INF) cout << "IMPOSSIBLE" << endl; else cout << res+1 << endl; } return 0; }
思路:bfs。POJ的题。。。。字符串运算还是有问题,直接贴上没测试的代码。
#include <iostream> #include <cstring> #include <queue> #include <sstream> using namespace std; const int maxn = 10; const int INF = 0x3f3f3f3f; char mm[maxn][maxn]; bool vis[maxn][maxn]; int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1}; struct Pos{ int x, y; string path; }; inline bool isValide(int x, int y){ return x>=0 and x<5 and y>=0 and y<5 and mm[x][y]=='0' and !vis[x][y]; } string bfs(){ queue<Pos> q; q.push({0, 0, "(0, 0)\n"}); vis[0][0] = true; while(q.size()){ Pos p = q.front(); q.pop(); if(p.x==4 and p.y==4) return p.path; for(int i = 0; i < 4; i++){ int nx = p.x + dx[i], ny = p.y + dy[i]; if(isValide(nx, ny)){ vis[nx][ny] = true; q.push({nx, ny, p.path + "(" + to_string(nx) + ", " + to_string(ny) + ")\n"}); } } } } int main(){ for(int i = 0; i < 5; i++) for(int j = 0; j < 5; j++) cin >> mm[i][j]; cout << bfs() << endl; return 0; }
思路:简单bfs或者dfs。
#include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 105; const int INF = 0x3f3f3f3f; int n, m; char mm[maxn][maxn]; int group[maxn][maxn]; int gid; struct Pos{ int x, y; }; void init(){ memset(group, 0, sizeof(group)); gid = 0; } void readData(){ for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) cin >> mm[i][j]; } inline bool isValide(int x, int y){ return x>=1 and x<=n and y>=1 and y<=m and !group[x][y] and mm[x][y]=='@'; } void bfs(int x, int y){ gid++; queue<Pos> q; q.push({x, y}); group[x][y] = gid; while(q.size()){ Pos p = q.front(); q.pop(); for(int i = -1; i <= 1; i++) for(int j = -1; j <= 1; j++){ int nx = p.x + i, ny = p.y + j; if(isValide(nx, ny)){ q.push({nx, ny}); group[nx][ny] = gid; } } } } int main(){ while(cin >> n >> m and n and m){ init(); readData(); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(!group[i][j] and mm[i][j]=='@') bfs(i, j); cout << gid << endl; } return 0; }
思路:bfs。和上面倒水那题差不多。 评论区看见了一种方法挺有意思的:先算出三杯水体积的公因数,然后三杯水的体积除以这个公因数。如果第一杯水为奇数就不可分,否则就可分,且倒水次数为数字减一。
证明:首先每杯水肯定是他们的公因子的倍数d,把d毫升水看作一个单位,那么三杯水可以陈放S/d,N/d,M/d单位的水,且N/d,M/d互质。显然,当S/d为奇数时无解;为偶数时,先考虑有一杯水的单位为1的情况,那么显然,S/d-1即为答案,倒一次水相当于倒多个单位水的情况,答案也为S/d-1。更严谨的证明以后有时间再写吧。
#include <iostream> using namespace std; int s, n, m; int gcd(int a, int b){ return a == 0 ? b : gcd(b%a, a); } int main(){ while(cin >> s >> n >> m and s){ int divisor = gcd(s, n); divisor = gcd(divisor, m); s /= divisor; if(s & 1) cout << "NO" << endl; else cout << s-1 << endl; } return 0; }
思路:bfs。
#include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 205; const int INF = 0x3f3f3f3f; int n, m; char mm[maxn][maxn]; int dist1[maxn][maxn], dist2[maxn][maxn]; struct Pos{ int x, y; }; int dx[]={1,-1,0,0}, dy[]={0,0,1,-1}; inline bool isValide(int x, int y, int (*dist)[maxn]){ return x>=1 and x<=n and y>=1 and y<=m and mm[x][y]!='#' and dist[x][y]==-1; } void bfs(int (*dist)[maxn], Pos st){ queue<Pos> q; q.push(st); dist[st.x][st.y] = 0; while(q.size()){ Pos p = q.front(); q.pop(); for(int i = 0; i < 4; i++){ int nx = p.x + dx[i], ny = p.y + dy[i]; if(isValide(nx, ny, dist)){ dist[nx][ny] = dist[p.x][p.y] + 1; q.push({nx, ny}); } } } } int main(){ while(cin >> n >> m){ memset(dist1, -1, sizeof(dist1)); memset(dist2, -1, sizeof(dist2)); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) cin >> mm[i][j]; for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(mm[i][j]=='Y') bfs(dist1, (Pos){i, j}); else if(mm[i][j]=='M') bfs(dist2, (Pos){i, j}); int res = INF; for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(mm[i][j]=='@' and dist1[i][j]!=-1 and dist2[i][j]!=-1) res = min(res, dist1[i][j] + dist2[i][j]); cout << res * 11 << endl; } return 0; }
现在kuangbin简单搜索专题就结束了,撒花完结。

浙公网安备 33010602011771号