typedef pair<int, int> pii; int n, m; //矩阵的长和宽 int vis[MAXN][MAXN]; //标记每个点属于哪一个连通块,0表示还问访问过 int mp[MAXN][MAXN]; //每个点的权值 int dx[4] = {0, 0, -1, 1}; int dy[4] = {-1, 1, 0, 0}; int cnt = 0; //连通块个数 int size[MAXN]; //连通块内部元素个数 voidbfs(int x, int y){ queue<pii> q; vis[x][y] = cnt; q.push({x, y}); while (q.size()) { int x = q.front().first, y = q.front().second; q.pop(); num++; //连通块内部个数加一 for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (vis[xx][yy] != 0) continue; //之前已访问过 if (xx > n || xx < 1 || yy > m || yy < 1) continue; //越界 if (mp[x][y] != mp[xx][yy]) continue; //权值不同 vis[xx][yy] = cnt; q.push({xx, yy}); } } size[cnt] = num; }
voidsolve(){ cin >> n >> m; //输入点权 for (int i = 1; i <= n; i++) for (int i = 1; i <= m; i++) if (!vis[i][j]) //发现了一个未搜索过的连通块 cnt++, bfs(x, j); }
int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; char s[MAXN][MAXN]; int vis[MAXN][MAXN]; ll n, m; int cnt = 0; typedef pair<int, int> pii; voidbfs(int x, int y){ queue<pii> q; vis[x][y] = 1; q.push({x, y}); while (q.size()) { int x = q.front().first, y = q.front().second; q.pop(); for (int i = 0; i < 8; i++) { int xx, yy; xx = x + dx[i]; yy = y + dy[i]; if (vis[xx][yy]) continue; if (s[xx][yy] == '.') continue; if (xx > n || xx < 1 || yy > m || yy < 1) continue; vis[xx][yy] = 1; q.push({xx, yy}); } } } voidsolve(){ cin >> n >> m; for (int i = 1; i <= n; i++) cin >> s[i] + 1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (!vis[i][j] && s[i][j] == 'W') { cnt++; bfs(i, j); } } } cout << cnt << endl; }
int n, m, cnt, ans; int dx[4] = {0, -1, 0, 1}; int dy[4] = {-1, 0, 1, 0}; int vis[MAXN][MAXN]; int mp[MAXN][MAXN]; //放大小
voidbfs(int x, int y){ int num = 0; vis[x][y] = cnt; queue<pii> q; q.push({x, y}); while (q.size()) { num++; int x = q.front().first, y = q.front().second; q.pop(); int val = mp[x][y]; for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (vis[xx][yy] != 0) continue; if (((val >> i) & 1)) continue; if (xx > n || xx < 1 || yy > m || yy < 1) continue; vis[xx][yy] = cnt; q.push({xx, yy}); } } ans = max(ans, num); }
voidsolve(){ cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> mp[i][j]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (!vis[i][j]) { cnt++; bfs(i, j); } } } cout << cnt << endl; cout << ans << endl; }
int mp[MAXN][MAXN], vis[MAXN][MAXN]; int n; int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; int sum1, sum2; //山峰和山谷个数
voidbfs(int x, int y){ vis[x][y] = 1; int f1 = 1; //是否有可能成为山峰 int f2 = 1; //是否有可能成为山谷 queue<pii> q; q.push({x, y}); while (q.size()) { int x = q.front().first, y = q.front().second; q.pop(); for (int i = 0; i < 8; i++) { int xx = x + dx[i], yy = y + dy[i];
if (xx > n || xx < 1 || yy > n || yy < 1) continue; if (mp[xx][yy] > mp[x][y]) { //不可能成为山峰了 f1 = 0; continue; } if (mp[xx][yy] < mp[x][y]) { //不可能成为山谷了 f2 = 0; continue; } if (vis[xx][yy]) continue; if (mp[xx][yy] == mp[x][y]) { vis[xx][yy] = 1; q.push({xx, yy}); } } } if (f1) { sum1++; // cout<<"top: "<<x<<" "<<y<<endl; } if (f2) { sum2++; // cout<<"bottom: "<<x<<" "<<y<<endl; } }
voidsolve(){ cin >> n; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) cin >> mp[i][j]; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (!vis[i][j]) bfs(i, j); cout << sum1 << " " << sum2 << endl; }
int n, m; //矩阵长宽 typedef pair<int, int> pii; int sx, sy, ex, ey; //起点和终点的坐标 int dis[MAXN][MAXN]; //起点到 (i,j) 的最短距离 int mp[MAXN][MAXN]; // 1表示有障碍物无法通过 int dx[4] = {0, 0, -1, 1}; int dy[4] = {-1, 1, 0, 0}; #define INF 0x3f3f3f3f
intbfs(int sx, int sy){ queue<pii> q; q.push({sx, sy}); while (q.size()) { int x = q.front().first, y = q.front().second; q.pop(); if (x == sx && y == sy) return dis[x][y]; //遍历到了终点 for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (xx > n || xx < 1 || yy > m || yy < 1) continue; //越界 if (dis[xx][yy] <= dis[x][y] + 1) continue; //其实没必要这么写,dis当vis用 if (mp[xx][yy]) continue; //有障碍物 dis[xx][yy] = dis[x][y] + 1; q.push({xx, yy}); } } }
int n, m; int dx[8] = {-2, -2, -1, -1, 1, 1, 2, 2}; int dy[8] = {-1, 1, -2, 2, -2, 2, -1, 1}; char s[MAXN][MAXN]; int dis[MAXN][MAXN]; boolok(int x, int y){ if (x > n || x < 1 || y > m || y < 1) returnfalse; returntrue; }
intbfs(pii se){ queue<pii> q; q.push(se); while (q.size()) { int x = q.front().first, y = q.front().second; q.pop(); for (int i = 0; i < 8; i++) { int xx = x + dx[i], yy = y + dy[i]; if (!ok(xx, yy)) continue; if (dis[xx][yy] <= dis[x][y] + 1) continue; if (s[xx][yy] == '*') continue; dis[xx][yy] = dis[x][y] + 1; q.push({xx, yy}); if (s[xx][yy] == 'H') return dis[xx][yy]; } } }
voidsolve(){ cin >> m >> n; for (int i = 1; i <= n; i++) { cin >> s[i] + 1; } pii se; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (s[i][j] == 'K') se = {i, j}, dis[i][j] = 0; else dis[i][j] = INF; } } cout << bfs(se); }
#include<bits/stdc++.h> usingnamespace std; #define ll long long #define MAXN 1005 typedef pair<int, int> pii; #define INF 0x3f3f3f3f int n, m; char s[MAXN][MAXN]; int dis[MAXN][MAXN]; int dx[4] = {0, 0, -1, 1}; int dy[4] = {-1, 1, 0, 0}; boolok(int x, int y){ if (x > n || x < 1 || y > m || y < 1) returnfalse; returntrue; }
voidbfs(){ queue<pii> q; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) if (s[i][j] == '1') q.push({i, j}), dis[i][j] = 0; while (q.size()) { int x = q.front().first, y = q.front().second; q.pop(); for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (!ok(xx, yy)) continue; if (dis[x][y] + 1 >= dis[xx][yy]) continue; dis[xx][yy] = dis[x][y] + 1; q.push({xx, yy}); } } }
voidsolve(){ cin >> n >> m; for (int i = 1; i <= n; i++) cin >> s[i] + 1; memset(dis, 0x3f, sizeof dis); bfs(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cout << dis[i][j] << " "; } cout << endl; } }
{% raw %} int n, k, a[MAXN]; int head[MAXN]; int tot; int isleaf[MAXN]; int d[MAXN], vis[MAXN]; // 0表示未访问,2表示自己可以走,1表示不能走 structEDGE { int to, next; } edge[MAXM]; voidadd_edge(int from, int to){ edge[++tot].to = to; edge[tot].next = head[from]; head[from] = tot; }
voidsolve(){ cin >> n >> m; for (int i = 0; i < n; i++) cin >> mp[i]; int ans = INF; ans = bfs(); if (ans == INF) cout << "NO SOLUTION" << endl; else cout << ans << endl; }
#include<bits/stdc++.h> usingnamespace std; #define ll long long string A, B; int n; string a[10], b[10]; //子串变换规则 unordered_map<string, int> disa; //起点到某个状态的距离 unordered_map<string, int> disb; //终点到某个状态的距离
intextend(queue<string> &q, unordered_map<string, int> &disa, unordered_map<string, int> &disb, string a[], string b[]){ //从当前队列仅扩展一层状态 int len = q.size(); for (int k = 0; k < len; k++) { //保证只扩展一层 string now = q.front(); q.pop(); if (disb.count(now)) //在对面找到了相同状态的点 return disa[now] + disb[now]; for (int i = 0; i < now.size(); i++) { //枚举替换开始位置 for (int j = 0; j < n; j++) { //枚举规则 if (now.substr(i, a[j].size()) == a[j]) { //能替换 string to = now.substr(0, i) + b[j] + now.substr(i + a[j].size()); //扩展状态 if (disa.count(to)) continue; //之前这个状态已经出现过了 disa[to] = disa[now] + 1; q.push(to); } } } } return11; //返回一个非法的数,表示没找到 }
intbfs(){ disa[A] = 0; disb[B] = 0; queue<string> qa, qb; // qa表示从起点开始搜,qb表示从终点开始搜 qa.push(A), qb.push(B); while (qa.size() && qb.size()) { //只有当qa和qb两个队列里都有东西时才能继续搜索 int t; //从小的队列开始扩展,能否出现另一个队列的元素,如果可以返回合法步数 if (qa.size() <= qb.size()) t = extend(qa, disa, disb, a, b); else t = extend(qb, disb, disa, b, a); //扩展的队列不同,参数位置也不同 if (t <= 10) return t; } return100; }
voidsolve(){ cin >> A >> B; while (cin >> a[n] >> b[n]) n++; int ans = bfs(); //开始双端搜索 if (ans > 10) cout << "NO ANSWER!" << endl; else cout << ans << endl; }
intget(string s){ //估值函数,当前状态中每个数与他目标位置的曼哈顿距离之和 int ans = 0; for (int i = 0; i < 9; i++) { if (s[i] == 'x') continue; int x = s[i] - '1'; ans += abs(x / 3 - i / 3) + abs(x % 3 - i % 3); } return ans; }
string bfs(string start){ priority_queue<pis, vector<pis>, greater<pis>> q; dis[start] = 0; q.push({get(start), start}); while (q.size()) { string now = q.top().second; //当前的状态 int step = dis[now]; //当前状态距离起点的步数 q.pop(); if (now == "12345678x") break; int pos = now.find('x'); int x = pos / 3, y = pos % 3; //记录'x'的坐标位置 string data = now; //保存原始数据,等会会变,并且记录答案时要用到前驱 for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (xx >= 3 || xx < 0 || yy >= 3 || yy < 0) continue; swap(now[x * 3 + y], now[xx * 3 + yy]); //枚举并交换'x'的位置,尝试扩展状态 if (!dis.count(now) || dis[now] > step + 1) { //如果还未更新过或者可以继续更新 dis[now] = step + 1; pre[now] = {data, op[i]}; q.push({dis[now] + get(now), now}); } now = data; //恢复现场 } } string end = "12345678x"; string ans = ""; while (start != end) { ans += pre[end].second; end = pre[end].first; } reverse(ans.begin(), ans.end()); return ans; }
voidsolve(){ string start, tmp; for (int i = 0; i < 9; i++) { char c; cin >> c; start += c; if (c != 'x') tmp += c; } int cnt = 0; //逆序对数量 for (int i = 0; i < 8; i++) for (int j = i + 1; j < 8; j++) if (tmp[i] > tmp[j]) cnt++; if (cnt & 1) cout << "unsolvable" << endl; else cout << bfs(start) << endl; }
K短路问题
这题的估值函数就比较好想,为当前点到终点的最短距离。
结论:优先队列弹出 k 次后,此时的 dis 为 k 小值
所以只需反着跑一遍最短路,求出每个点到终点的最小值。再正着跑一遍 A-STAR ,记录弹出顶点为 end 的次数。当次数等于 k 时,就把现在的 distance 输出。遍历时不用考虑是否已经更新过(A-STAR 算法不用判重),是否满足松弛条件(除了终点,A-STAR 算法无法保证最优解),一股脑往优先队列里塞扩展状态。
int n, m; int s, e, k; int vis[MAXN], dis[MAXN]; int cnt[MAXN]; //剪枝玄学优化,能加速 int head[MAXN]; int rhead[MAXN]; int tot; structEDGE { int to, next, val; } edge[MAXM]; voidadd_edge(int h[], int from, int to, int val){ edge[++tot].to = to; edge[tot].val = val; edge[tot].next = h[from]; h[from] = tot; }
voiddij(int s){ memset(dis, 0x3f, sizeof dis); dis[s] = 0; priority_queue<pii, vector<pii>, greater<pii>> q; q.push({0, s}); while (q.size()) { int now = q.top().second; int distance = q.top().first; q.pop(); if (vis[now]) continue; vis[now] = 1; for (int i = rhead[now]; i; i = edge[i].next) { //有向边,反着遍历求最短路 int to = edge[i].to; int val = edge[i].val; if (dis[to] > distance + val) { dis[to] = distance + val; q.push({dis[to], to}); } } } }
intastar(int s, int e, int k){ priority_queue<piii, vector<piii>, greater<piii>> q; q.push({dis[s], {0, s}}); while (q.size()) { int distance = q.top().second.first; int now = q.top().second.second; q.pop(); cnt[now]++; if (cnt[e] == k) return distance; //此时的值为k短路 for (int i = head[now]; i; i = edge[i].next) { int to = edge[i].to; int val = edge[i].val; if (cnt[to] < k) //剪枝玄学优化 q.push({dis[to] + distance + val, {distance + val, to}}); } } return-1; //没有k短路 }
voidsolve(){ cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v, w; cin >> u >> v >> w; add_edge(head, u, v, w), add_edge(rhead, v, u, w); } cin >> s >> e >> k; if (s == e) k++;
int n, m; int vis[MAXN][MAXN]; char mp[MAXN][MAXN]; int cnt = 0; int sx, sy; int dx[4] = {0, 0, -1, 1}; int dy[4] = {1, -1, 0, 0};
voiddfs(int x, int y){ vis[x][y] = 1; for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (xx < 1 || x > n || yy < 1 || yy > m) continue; if (vis[xx][yy]) continue; if (mp[xx][yy] == '#') continue; dfs(xx, yy); } }
voidsolve(){ init(); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { cin >> mp[i][j]; if (mp[i][j] == '@') sx = i, sy = j; } dfs(sx, sy); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cnt += vis[i][j]; cout << cnt << endl; }
intmain(){ while (cin >> m >> n) { if (!n && !m) break; solve(); } return0; }
int n, m, a, b, ans; int vis[20][20]; int dx[8] = {2, 1, -1, -2, -2, -1, 1, 2}; int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1}; voiddfs(int x, int y, int step){ if (step == n * m) { ans++; return; } for (int i = 0; i < 8; i++) { int xx = x + dx[i]; int yy = y + dy[i]; if (xx >= 0 && xx < n && yy >= 0 && yy < m && vis[xx][yy] == 0) { vis[xx][yy] = 1; //标记 dfs(xx, yy, step + 1); vis[xx][yy] = 0; //回溯 } } }
voidsolve(){ ans = 0; memset(vis, 0, sizeof vis); cin >> n >> m >> a >> b; vis[a][b] = 1; //标记起点 dfs(a, b, 1); cout << ans << endl; }
int n; int a[MAXN]; int mp[MAXN][MAXN]; //存放每个组内存放的数的序号 int ans = MAXN; int vis[MAXN]; //看看某个数有没有被放进组内 ll gcd(ll a, ll b){ if (a < b) swap(a, b); ll tmp; while (b) { tmp = b; b = a % b; a = tmp; } return a; }
boolcheck(int mp[], int s, int start){ for (int j = 0; j < start; j++) { if (gcd(a[mp[j]], s) > 1) returnfalse; } returntrue; }
voiddfs(int now, int g_cur, int t_cur, int start){ //now为组数 //g_cur为当前组内搜索到的数的序号 //t_cur为搜索过的数字 //start为当前组开始搜索的数的序号 if (now >= ans) //最优性剪枝 return; if (t_cur == n) { ans = min(ans, now); } bool flag = true; for (int i = start; i < n; i++) { if (!vis[i] && check(mp[now], a[i], g_cur)) { //如果与组内元素都互质 vis[i] = 1; mp[now][g_cur] = i; dfs(now, g_cur + 1, t_cur + 1, i + 1); vis[i] = 0; flag = false; } } if (flag) //当前组内找不到,找下一个组 dfs(now + 1, 0, t_cur, 0); }
voidsolve(){ cin >> n; for (int i = 0; i < n; i++) cin >> a[i]; dfs(1, 0, 0, 0); cout << ans << endl; }
剪枝
常用策略
优化搜索顺序,一般情况下,优先搜索分支较少的节点
排除等效冗余,不搜索重复状态
可行性剪枝,遇到非法情况提前退出
最优性剪枝,计算最值时大于等于当前的最优解提前退出
记忆化搜索 (DP)
题目: 索道上的缆车最大承重量为 W ,而 N 只小猫的重量已给出,每辆缆车上的小猫重量之和不超过 W 。问最少要租多少缆车。
int n, mx; int a[MAXN]; int rem[MAXN]; //某辆车剩余的空间 int ans = MAXN;
voiddfs(int now, int k){ //当前的猫,当前拥有的车的数量 if (k >= ans) //最优性剪枝 return; if (now == n + 1) { ans = k; return; } //两种状态,塞进一辆缆车或者新租一辆缆车 for (int i = 1; i <= k; i++) { if (a[now] <= rem[i]) { rem[i] -= a[now]; dfs(now + 1, k); rem[i] += a[now]; } } rem[k + 1] -= a[now]; dfs(now + 1, k + 1); rem[k + 1] += a[now]; }
voidsolve(){ cin >> n >> mx; for (int i = 1; i <= n; i++) cin >> a[i]; sort(a + 1, a + 1 + n, greater<>()); for (int i = 1; i <= n; i++) rem[i] = mx; dfs(1, 1); cout << ans << endl; }
ll a, b; bool flag; int path[MAXN]; ll get(ll x){ ll ans = 1; for (ll i = 2; i <= x; i++) ans *= i; return ans; } booldfs(ll num, int now, int depth){ if (num == b) { printf("%lld\n", now); flag = true; returntrue; } if (now == depth) returnfalse; //迭代加深 if (dfs(get(num), now + 1, depth)) returntrue; ll tmp = sqrt(num); if (dfs(tmp, now + 1, depth)) returntrue; if (tmp * tmp != num) { if (dfs(tmp + 1, now + 1, depth)) returntrue; } returnfalse; }
voidsolve(){ a = read(), b = read(); int depth = 1; if (a == b) { puts("0"); return; } flag = false; while (depth <= 7 && !dfs(a, 0, depth)) { depth++; } if (!flag) puts("-1"); }