算法进阶-搜索
算法进阶-搜索
-
题目描述:给定一张N个点M条边的有向无环图,分别统计从每个点除法能够到达的点的数量
**数据规模:**1 <= n <= 3e4
**分析:**这里我们可以使用拓扑排序根据入边对所有点进行排序,排序后我们按照逆序,结合状态压缩将相连接的点存入bitset中(使用或操作),最后我们从1开始到n返回二进制中1的个数,即是可到达点的个数。这里只要是这两个点相连接,后一个点就和前一个点的所有位置进行或运算
题解: 见分析
TIPS:java中集成有BitSet,set是将对应位置设为真,cardinality返回当前二进制中1的个数
时间复杂度:O(N*M)
#include<iostream> #include<bitset> #include<cstring> #include<queue> using namespace std;const int N = 3e4 + 10; int n, m; int h[N], e[N], ne[N], idx; int d[N], seq[N];bitset<N> f[N];void add(int u, int v){e[idx] = v, ne[idx] = h[u], h[u] = idx++; }void topsort(){queue<int> q;for(int i = 1; i <= n; i++){if(!d[i]) q.push(i);}int k = 0;while(q.size()){int t = q.front();q.pop();seq[k++] = t;for(int i = h[t]; ~i; i = ne[i]){int j = e[i];if(!--d[j]) q.push(j);}} } int main(){cin >> n >> m;memset(h, -1, sizeof h);for(int i = 0; i < m; i++){int u, v;cin >> u >> v;add(u, v);d[v]++;}topsort();for(int i = n - 1; ~i; i--){int j = seq[i];f[j][j] = 1;for(int p = h[j]; ~p; p = ne[p]) f[j] |= f[e[p]];}for(int i = 1; i <= n; i++) cout << f[i].count() << endl;return 0; }
import java.util.*;public class Main {public static void main(String[] args) {new Main().run();}final int N = (int) (3e4 + 10);int[] h = new int[N], e = new int[N], next = new int[N];int idx = 0;int[] d = new int[N], seq = new int[N];int n, m;BitSet[] sets = new BitSet[N];void topSort(){Queue<Integer> q = new ArrayDeque<>();int idx = 0;for(int i = 1; i <= n; i++) if(d[i] == 0) q.add(i);while(!q.isEmpty()){int u = q.poll();seq[idx++] = u;for(int i = h[u]; i != -1; i = next[i]){int j = e[i];if(--d[j] == 0) q.add(j);}}}void add(int u, int v){e[idx] = v;next[idx] = h[u];h[u] = idx++;}void run(){Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 0; i <= n; i++) sets[i] = new BitSet();Arrays.fill(h, -1);for(int i = 0; i < m; i++){int u = sc.nextInt(), v = sc.nextInt();add(u, v);d[v]++;}topSort();for(int i = n - 1; i >= 0; i--){int j = seq[i];sets[j].set(j);for(int p = h[j]; p != -1; p = next[p]){int edge = e[p];sets[j].or(sets[edge]);}}for(int i = 1; i <= n; i++) System.out.println(sets[i].cardinality());sc.close();} }
-
题目描述:给定n个商品,和对应商品的重量。我们想将商品放入包裹,包裹可承载的重量之和不能超过w。现在我们想知道最少需要多少包裹,可以使全部包裹放入
**数据规模:**1 <= n <= 18
**分析:**这里要递归来扫描整个状态空间,其中有两个变量,当前商品的编号以及包裹的编号。我们想让整棵树的分支尽可能少,因此在上面要尽量放大的商品。
题解: 见分析
TIPS:无
时间复杂度:O(2^n)
#include<iostream> #include<algorithm>using namespace std;const int N = 20; int n, m; int cat[N], sum[N]; int ans = N;/**** @param u 猫的编号* @param k 缆车的编号*/ void dfs(int u, int k){if(k >= ans) return;//剪枝-若当前缆车编号大于或等于ans则跳出if(u == n){//边界条件ans = k;return;}//1.若当前猫的重量可以放入for(int i = 0; i < k; i++){if(cat[u] + sum[i] <= m){sum[i] += cat[u];dfs(u + 1, k);sum[i] -= cat[u];}}//2.放入新的缆车sum[k] = cat[u];dfs(u + 1, k + 1);sum[k] = 0; } int main(){cin >> n >> m;for(int i = 0; i < n; i++){cin >> cat[i];}sort(cat, cat + n);reverse(cat, cat + n);dfs(0, 0);cout << ans << endl;return 0; }
import java.util.Arrays; import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 20;int n, m;Integer[] cat = new Integer[N];int[] sum = new int[N];int ans = N;/*** 递归* @param u 当前猫* @param k 当前缆车*/void dfs(int u, int k){//剪枝if(k >= ans) return;//边界条件if(u == n){ans = k;return;}//1.若当前猫可以放入for(int i = 0; i < k; i++){if(cat[u] + sum[i] <= m){sum[i] += cat[u];dfs(u + 1, k);sum[i] -= cat[u];}}//2.放入新的缆车sum[k] = cat[u];dfs(u + 1, k + 1);sum[k] = 0;}void run(){Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for(int i = 0; i < n; i++) cat[i] = sc.nextInt();Arrays.sort(cat, 0, n, (a, b) -> b - a);dfs(0, 0);System.out.println(ans);} }
-
题目描述:给出数独的所有状态,请将它填满。
**数据规模:**n = 9
**分析:**这里数据量很大,要预处理不同数二进制下1的个数,和对应二进制次幂的指数,这样我们就可以在O(1)时间内求得对应值。我们进行搜索时,优先从少的情况开始搜索这样就大大减少了搜索范围。
题解:
- 对row、col和cell进行初始化,将对应二进制所有的位都置为1代表未选状态。
- 枚举字符串的所有位置,看有多少格子要填以及将对应二进制位的数字置为1.这里需要注意字符串中是19,二进制对应的是08要转换
- 进行递归搜索,我们枚举所有位置看哪个位置可选项最少即填的最多,从这里开始搜索
- 搜索时每次选一个可选项,选后要对二进制状态以及字符数组进行修改,选后向下搜索。最后进行回溯将状态还原。边界条件是: cnt = 0即全部填满即返回。否则返回false
TIPS:需要使用到lowbit运算、状态压缩、递归优化
时间复杂度:未知
#include<iostream>using namespace std; const int N = 9;int ones[1 << N], map[1 << N];//ones记录一个状态值中1的个数,map记录logn为多少 int row[N], col[N], cell[N][N]; char str[100]; /*** 获取最后一个1* @param x* @return*/ inline int lowbit(int x){return x & -x; }void init(){for(int i = 0; i < N; i++) row[i] = col[i] = (1 << N) - 1;//初始状态时全为111111111for(int i = 0; i < 3; i++)for(int j = 0; j < 3; j++) cell[i][j] = (1 << N) - 1;//同理 }/*** 可选方案的交集* @param x* @param y* @return*/ inline int get(int x, int y){return row[x] & row[y] & cell[x / 3][y / 3]; }bool dfs(int cnt){if(!cnt) return true;//找出可选方案数最少的空格int minv = 10;int x, y;for(int i = 0; i < N; i++)for(int j = 0; j < N; j++)if(str[i * N + j] == '.'){int t = ones[get(i, j)];//寻找合法数字的个数if(t < minv){//寻找状态空位中最少的状态minv = t;x = i, y = j;//记录空位最少的下标}}for(int i = get(x, y); i; i -= lowbit(i)){int t = map[lowbit(i)];//修改状态row[x] -= 1 << t;col[y] -= 1 << t;cell[x / 3][y / 3] -= 1 << t;str[x * N + y] = '1' + t;if(dfs(cnt - 1)) return true;row[x] += 1 << t;col[y] += 1 << t;cell[x / 3][y / 3] += 1 << t;str[x * N + y] = '.';}return false; } int main(){for(int i = 0; i < N; i++) map[1 << i] = i;//求lognfor(int i = 0; i < 1 << N; i++){int s = 0;for(int j = i; j; j -= lowbit(j)) s++;ones[i] = s;}while(cin >> str, str[0] != 'e'){init();int cnt = 0;for(int i = 0, k = 0; i < N; i++){for(int j = 0; j < N; j++, k++){if(str[k] != '.'){int t = str[k] - '1';row[i] -= 1 << t;col[j] -= 1 << t;cell[i / 3][j / 3] -= 1 << t;}else cnt++;}}dfs(cnt);}cout << str << endl;return 0; }
import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 9;//ones记录对应二进制中1的个数,map记录对应数的对数int[] ones = new int[1 << N], map = new int[1 << N];int[] row = new int[N], col = new int[N];int[][] cell = new int[3][3];char[] str;/*** 取出最低位的1以及后续数* @param x* @return*/int lowbit(int x){return x & -x;}/*** 初始化row、col和cell*/void init(){for(int i = 0; i < N; i++) row[i] = col[i] = (1 << N) - 1;//对应位全位1for(int i = 0; i < 3; i++)for(int j = 0; j < 3; j++) cell[i][j] = (1 << N) - 1;}/*** 求可选方案的交集* @param x* @param y* @return*/int get(int x, int y){return row[x] & col[y] & cell[x / 3][y / 3];}boolean dfs(int cnt){//边界条件if(cnt == 0) return true;//找出可选方案数最少的空格int min = 10;int x = 0, y = 0;//扫描对应位置,看是否位空格for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(str[i * N + j] == '.'){int t = ones[get(i, j)];if(t < min){min = t;x = i;y = j;}}}}//每次拿掉一种可能for(int i = get(x, y); i > 0; i -= lowbit(i)){int t = map[lowbit(i)];//求对应的二进制的对数//修改状态row[x] -= 1 << t;col[y] -= 1 << t;//0~8转化位1~9cell[x / 3][y / 3] -= 1 << t;str[x * N + y] = (char)('1' + t);if(dfs(cnt - 1)) return true;//还原row[x] += 1 << t;col[y] += 1 << t;cell[x / 3][y / 3] += 1 << t;str[x * N + y] = '.';}return false;}void run(){for(int i = 0; i < N; i++) map[1 << i] = i;//预处理for(int i = 0; i < 1 << N; i++){int s = 0;for(int j = i; j > 0; j -= lowbit(j)) s++;ones[i] = s;}Scanner sc = new Scanner(System.in);while(true){String s = sc.next();if(s.equals("end")) break;str = s.toCharArray();init();int cnt = 0;//记录有多少格子要填for(int i = 0, k = 0; i < N; i++){for(int j = 0; j < N; j++, k++){//将对应二进制变位0if(str[k] != '.'){//将1~9转化为0~8int t = str[k] - '1';row[i] -= 1 << t;col[j] -= 1 << t;cell[i / 3][j / 3] -= 1 << t;}else cnt++;}}dfs(cnt);for(int i = 0; i < str.length; i++) System.out.print(str[i]);System.out.println();}sc.close();} }
-
题目描述:给出数独的所有状态,请将它填满。
**数据规模:**n = 16
分析:
- 搜索顺序:每次找到一个空格,枚举空格选择哪个字母,然后往下递归。边界条件:所有空格都填完。
- 状态的存储:state[x] [y],表示x行y列这个格子可以填哪些数(015),我们用16位二进制来表示,所有状态为02^16-1
优化:
- 对于每个空格,如果不能填任何一个字母,无解;如果只能填一个字母,那么直接填上。
- 对于每一行:如果某个字母不能出现在任何一个位置,无解;如果某个字母只有一个位置可以填,则直接填上;
- 对于每一列,同理
- 对于每一个16宫格,类似
- 每次选择空格时,选择备选方案最少得格子来填。
题解:
- 对row、col和cell进行初始化,将对应二进制所有的位都置为1代表未选状态。
- 枚举字符串的所有位置,看有多少格子要填以及将对应二进制位的数字置为1.这里需要注意字符串中是19,二进制对应的是08要转换
- 进行递归搜索,我们枚举所有位置看哪个位置可选项最少即填的最多,从这里开始搜索
- 搜索时每次选一个可选项,选后要对二进制状态以及字符数组进行修改,选后向下搜索。最后进行回溯将状态还原。边界条件是: cnt = 0即全部填满即返回。否则返回false
TIPS:需要使用到lowbit运算、状态压缩、递归优化
时间复杂度:未知
#include<iostream> #include<cstring>using namespace std; const int N = 16, M = N * N + 1; int ones[1 << N], log_arr[1 << N]; int state[N][N]; char str[N][N + 1]; int bstate[M][N][N], bstate2[M][N][N]; char bstr[M][N][N + 1];/*** lowbit运算取出二进制下最低位的1* @param x* @return*/ inline int lowbit(int x){return x & -x; }/*** 将字符串数组和state的状态进行变更* @param x* @param y* @param c*/ void draw(int x, int y, int c){str[x][y] = c + 'A';//更改字符串数组为对应字母for(int i = 0; i < N; i++){state[x][i] &= ~(1 << c);//将二进制对应位置置为0表示已选state[i][y] &= ~(1 << c);}//将16宫格对应位置置为0,表示已选int sx = x / 4 * 4, sy = y / 4 * 4;for(int i = 0; i < 4; i++)for(int j = 0; j < 4; j++)state[sx + i][sy + j] &= ~(1 << c);//表示该位置选了cstate[x][y] = 1 << c; }bool dfs(int cnt){//边界条件当cnt = 0即全部找到返回if(!cnt) return true;//备份cnt、state、str后续失败后还原int kcnt = cnt;memcpy(bstate[kcnt], state, sizeof state);memcpy(bstr[kcnt], str, sizeof str);//1.剪枝,对于每个空格若不能填任何数,则返回false,若只能填一个数就填上for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(str[i][j] == '-'){//若可以填数//获取当前状态,即可选项int t = state[i][j];//若都不可选即全为0,还原后直接返回falseif(!t){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}//若只有一个可选就填上if(ones[t] == 1){draw(i, j, log_arr[t]);cnt--;}}}}//剪枝2.对于每行,若某个字母不能出现在任何一个位置,则返回false,若某个字母只有一个位置可填,则直接填for(int i = 0; i < N; i++){//这里sor填的是所有备选方案的并集,sand是所有备选方案就交集,用来找某个字母只有一个位置可填int sor = 0, sand = (1 << N) - 1;int drawn = 0;//表示已经填上的字母for(int j = 0; j < N; j++){int s = state[i][j];sand &= ~(sor & s);//将不符合要求的删掉sor |= s;//求备选方案的并if(str[i][j] != '-') drawn |= s;//将当前位置信息保存在drawn中}//该改行不能选完所有数,就还原结束if(sor != (1 << N) - 1){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}//枚举该数字,看有哪个位置可填for(int j = sand; j; j -= lowbit(j)){int t = lowbit(j);//若正好当前数未填过if(!(drawn & t)){for(int k = 0; k < N; k++){//若该位置可填就填上if(state[i][k] & t){draw(i, k, log_arr[t]);cnt--;break;}}}}}//对于每一列,同理for(int i = 0; i < N; i++){int sor = 0, sand = (1 << N) - 1;int drawn = 0;for(int j = 0; j < N; j++){int s = state[j][i];sand &= ~(sor & s);sor |= s;if(str[j][i] != '-') drawn |= s;}if(sor != (1 << N) - 1){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}for(int j = sand; j; j -= lowbit(j)){int t = lowbit(j);if(!(drawn & t)){for(int k = 0; k < N; k++){if(state[k][i] & t){draw(k, i, log_arr[t]);cnt--;break;}}}}}//对于每个16宫格for(int i = 0; i < N; i++){int sor = 0, sand = (1 << N) - 1;int drawn = 0;for(int j = 0; j < N; j++){int sx = i / 4 * 4, sy = i % 4 * 4;int dx = j / 4, dy = j % 4;int s = state[sx + dx][sy + dy];sand &= ~(sor & s);sor |= s;if(str[sx + dx][sy + dy] != '-') drawn |= state[sx + dx][sy + dy];}if(sor != (1 << N) - 1){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}for(int j = sand; j; j -= lowbit(j)){int t = lowbit(j);if(!(drawn & t)){for(int k = 0; k < N; k++){int sx = i / 4 * 4, sy = i % 4 * 4;int dx = k / 4, dy = k % 4;if(state[sx + dx][sy + dy] & t){draw(sx + dx, sy + dy, log_arr[t]);cnt--;break;}}}}}//若都填满了if(!cnt) return true;//每次选最少得格子来填int x, y, s = 100;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){int t = state[i][j];if(str[i][j] == '-' && ones[t] < s){s = ones[t];x = i, y = j;}}}//这里先备份memcpy(bstate2[kcnt], state, sizeof state);for(int i = state[x][y]; i; i -= lowbit(i)){memcpy(state, bstate2[kcnt], sizeof state);draw(x, y, log_arr[lowbit(i)]);if(dfs(cnt - 1)) return true;}//还原memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false; }int main(){for(int i = 0; i < N; i++) log_arr[1 << i] = i;//初始化log数组存对应数的对数for(int i = 0; i < 1 << N; i++)for(int j = i; j; j -= lowbit(j)) ones[i]++;//求所有数中1的个数/*** 输入字符串,初始化state数组,状态全为1,即为都可选*/while(cin >> str[0]){for(int i = 1; i < N; i++) cin >> str[i];for(int i = 0; i < N; i++)for(int j = 0; j < N; j++)state[i][j] = (1 << N) - 1;int cnt = 0;//有多少数要填//根据字母对状态数组将进行更改for(int i = 0; i < N; i++){for(int j = 0; j < N; j++)if(str[i][j] != '-') draw(i, j, str[i][j] - 'A');else cnt++;}//递归求答案dfs(cnt);//输出for(int i = 0; i < N; i++) cout << str[i] << endl;puts("");}return 0; } #include<iostream> #include<cstring>using namespace std; const int N = 16, M = N * N + 1; int ones[1 << N], log_arr[1 << N]; int state[N][N]; char str[N][N + 1]; int bstate[M][N][N], bstate2[M][N][N]; char bstr[M][N][N + 1];/*** lowbit运算取出二进制下最低位的1* @param x* @return*/ inline int lowbit(int x){return x & -x; }/*** 将字符串数组和state的状态进行变更* @param x* @param y* @param c*/ void draw(int x, int y, int c){str[x][y] = c + 'A';//更改字符串数组为对应字母for(int i = 0; i < N; i++){state[x][i] &= ~(1 << c);//将二进制对应位置置为0表示已选state[i][y] &= ~(1 << c);}//将16宫格对应位置置为0,表示已选int sx = x / 4 * 4, sy = y / 4 * 4;for(int i = 0; i < 4; i++)for(int j = 0; j < 4; j++)state[sx + i][sy + j] &= ~(1 << c);//表示该位置选了cstate[x][y] = 1 << c; }bool dfs(int cnt){//边界条件当cnt = 0即全部找到返回if(!cnt) return true;//备份cnt、state、str后续失败后还原int kcnt = cnt;memcpy(bstate[kcnt], state, sizeof state);memcpy(bstr[kcnt], str, sizeof str);//1.剪枝,对于每个空格若不能填任何数,则返回false,若只能填一个数就填上for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(str[i][j] == '-'){//若可以填数//获取当前状态,即可选项int t = state[i][j];//若都不可选即全为0,还原后直接返回falseif(!t){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}//若只有一个可选就填上if(ones[t] == 1){draw(i, j, log_arr[t]);cnt--;}}}}//剪枝2.对于每行,若某个字母不能出现在任何一个位置,则返回false,若某个字母只有一个位置可填,则直接填for(int i = 0; i < N; i++){//这里sor填的是所有备选方案的并集,sand是所有备选方案就交集,用来找某个字母只有一个位置可填int sor = 0, sand = (1 << N) - 1;int drawn = 0;//表示已经填上的字母for(int j = 0; j < N; j++){int s = state[i][j];sand &= ~(sor & s);//将不符合要求的删掉sor |= s;//求备选方案的并if(str[i][j] != '-') drawn |= s;//将当前位置信息保存在drawn中}//该改行不能选完所有数,就还原结束if(sor != (1 << N) - 1){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}//枚举该数字,看有哪个位置可填for(int j = sand; j; j -= lowbit(j)){int t = lowbit(j);//若正好当前数未填过if(!(drawn & t)){for(int k = 0; k < N; k++){//若该位置可填就填上if(state[i][k] & t){draw(i, k, log_arr[t]);cnt--;break;}}}}}//对于每一列,同理for(int i = 0; i < N; i++){int sor = 0, sand = (1 << N) - 1;int drawn = 0;for(int j = 0; j < N; j++){int s = state[j][i];sand &= ~(sor & s);sor |= s;if(str[j][i] != '-') drawn |= s;}if(sor != (1 << N) - 1){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}for(int j = sand; j; j -= lowbit(j)){int t = lowbit(j);if(!(drawn & t)){for(int k = 0; k < N; k++){if(state[k][i] & t){draw(k, i, log_arr[t]);cnt--;break;}}}}}//对于每个16宫格for(int i = 0; i < N; i++){int sor = 0, sand = (1 << N) - 1;int drawn = 0;for(int j = 0; j < N; j++){int sx = i / 4 * 4, sy = i % 4 * 4;int dx = j / 4, dy = j % 4;int s = state[sx + dx][sy + dy];sand &= ~(sor & s);sor |= s;if(str[sx + dx][sy + dy] != '-') drawn |= state[sx + dx][sy + dy];}if(sor != (1 << N) - 1){memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false;}for(int j = sand; j; j -= lowbit(j)){int t = lowbit(j);if(!(drawn & t)){for(int k = 0; k < N; k++){int sx = i / 4 * 4, sy = i % 4 * 4;int dx = k / 4, dy = k % 4;if(state[sx + dx][sy + dy] & t){draw(sx + dx, sy + dy, log_arr[t]);cnt--;break;}}}}}//若都填满了if(!cnt) return true;//每次选最少得格子来填int x, y, s = 100;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){int t = state[i][j];if(str[i][j] == '-' && ones[t] < s){s = ones[t];x = i, y = j;}}}//这里先备份memcpy(bstate2[kcnt], state, sizeof state);for(int i = state[x][y]; i; i -= lowbit(i)){memcpy(state, bstate2[kcnt], sizeof state);draw(x, y, log_arr[lowbit(i)]);if(dfs(cnt - 1)) return true;}//还原memcpy(state, bstate[kcnt], sizeof state);memcpy(str, bstr[kcnt], sizeof str);return false; }int main(){for(int i = 0; i < N; i++) log_arr[1 << i] = i;//初始化log数组存对应数的对数for(int i = 0; i < 1 << N; i++)for(int j = i; j; j -= lowbit(j)) ones[i]++;//求所有数中1的个数/*** 输入字符串,初始化state数组,状态全为1,即为都可选*/while(cin >> str[0]){for(int i = 1; i < N; i++) cin >> str[i];for(int i = 0; i < N; i++)for(int j = 0; j < N; j++)state[i][j] = (1 << N) - 1;int cnt = 0;//有多少数要填//根据字母对状态数组将进行更改for(int i = 0; i < N; i++){for(int j = 0; j < N; j++)if(str[i][j] != '-') draw(i, j, str[i][j] - 'A');else cnt++;}//递归求答案dfs(cnt);//输出for(int i = 0; i < N; i++) cout << str[i] << endl;puts("");}return 0; }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter;import java.util.StringTokenizer;public class Main {public static void main(String[] args) {new Main().run();}final int N = 16, M = N * N + 1;int[] ones = new int[1 << N], log = new int[1 << N];int[][] state = new int[N][N];char[][] str = new char[N][N + 1];int[][][] bstate = new int[M][N][N], bstate2 = new int[M][N][N];char[][][] bstr = new char[M][N][N + 1];int lowbit(int x){return x & -x;}void draw(int x, int y, int c){str[x][y] = (char)(c + 'A');for(int i = 0; i < N; i++){state[x][i] &= ~(1 << c);state[i][y] &= ~(1 << c);}int sx = x / 4 * 4, sy = y / 4 * 4;for(int i = 0; i < 4; i++)for(int j = 0; j < 4; j++)state[sx + i][sy + j] &= ~(1 << c);state[x][y] = 1 << c;}boolean dfs(int cnt){if(cnt == 0) return true;int kcnt = cnt;for(int i = 0; i < N; i++) {System.arraycopy(str[i], 0, bstr[kcnt][i], 0, N);System.arraycopy(state[i], 0, bstate[kcnt][i],0, N);}for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(str[i][j] == '-'){int t = state[i][j];if(t == 0){for(int k = 0; k < N; k++) {System.arraycopy(bstr[kcnt][k], 0, str[k], 0, N);System.arraycopy(bstate[kcnt][k], 0, state[k],0, N);}return false;}if(ones[t] == 1){draw(i, j, log[t]);cnt--;}}}}//行for(int i = 0; i < N; i++){int sor = 0, sand = (1 << N) - 1;int drawn = 0;for(int j = 0; j < N; j++){int s = state[i][j];sand &= ~(sor & s);sor |= s;if(str[i][j] != '-') drawn |= s;}if(sor != (1 << N) - 1){for(int k = 0; k < N; k++){System.arraycopy(bstr[kcnt][k], 0, str[k], 0, N);System.arraycopy(bstate[kcnt][k], 0, state[k],0, N);}return false;}for(int j = sand; j > 0; j-= lowbit(j)){int t = lowbit(j);if((drawn & t) == 0){for(int k = 0; k < N; k++){if((state[i][k] & t) > 0){draw(i, k, log[t]);cnt--;break;}}}}}//列for(int i = 0; i < N; i++){int sor = 0, sand = (1 << N) - 1;int drawn = 0;for(int j = 0; j < N; j++){int s = state[j][i];sand &= ~(sor & s);sor |= s;if(str[j][i] != '-') drawn |= s;}if(sor != (1 << N) - 1){for(int k = 0; k < N; k++){System.arraycopy(bstr[kcnt][k], 0, str[k], 0, N);System.arraycopy(bstate[kcnt][k], 0, state[k],0, N);}return false;}for(int j = sand; j > 0; j-= lowbit(j)){int t = lowbit(j);if((drawn & t) == 0){for(int k = 0; k < N; k++){if((state[k][i] & t) > 0){draw(k, i, log[t]);cnt--;break;}}}}}//16宫格for(int i = 0; i < N; i++){int sor = 0, sand = (1 << N) - 1;int drawn = 0;for(int j = 0; j < N; j++){int sx = i / 4 * 4, sy = i % 4 * 4;int dx = j / 4, dy = j % 4;int s = state[sx + dx][sy + dy];sand &= ~(sor & s);sor |= s;if(str[sx + dx][sy + dy] != '-') drawn |= state[sx + dx][sy + dy];}if(sor != (1 << N) - 1){for(int k = 0; k < N; k++){System.arraycopy(bstr[kcnt][k], 0, str[k], 0, N);System.arraycopy(bstate[kcnt][k], 0, state[k],0, N);}return false;}for(int j = sand; j > 0; j -= lowbit(j)){int t = lowbit(j);if((drawn & t) == 0){for(int k = 0; k < N; k++){int sx = i / 4 * 4, sy = i % 4 * 4;int dx = k / 4, dy = k % 4;if((state[sx + dx][sy + dy] & t) > 0){draw(sx + dx, sy + dy, log[t]);cnt--;break;}}}}}if(cnt == 0) return true;int x = 0, y = 0, s = 100;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){int t = state[i][j];if(str[i][j] == '-' && ones[t] < s){s = ones[t];x = i;y = j;}}}for(int k = 0; k < N; k++){System.arraycopy(state[k], 0, bstate2[kcnt][k], 0, N);}for(int i = state[x][y]; i > 0; i -= lowbit(i)){for(int k = 0; k < N; k++){System.arraycopy(bstate2[kcnt][k], 0, state[k], 0, N);}draw(x, y, log[lowbit(i)]);if(dfs(cnt - 1)) return true;}for(int i = 0; i < N; i++){System.arraycopy(bstate[kcnt][i], 0, state[i], 0, N);System.arraycopy(bstr[kcnt][i], 0, str[i], 0, N);}return false;}StringTokenizer stringTokenizer;BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));String next(){while(stringTokenizer == null || !stringTokenizer.hasMoreElements()){try {stringTokenizer = new StringTokenizer(bufferedReader.readLine());} catch (IOException e) {e.printStackTrace();}}return stringTokenizer.nextToken();}void run(){for(int i = 0; i < N; i++) log[1 << i] = i;for(int i = 0; i < N; i++)for(int j = i; j > 0; j -= lowbit(j)) ones[i]++;PrintWriter out = new PrintWriter(System.out);while(true){for(int i = 0; i < N; i++){char[] line = next().toCharArray();for(int j = 0; j < N; j++){str[i][j] = line[j];}}for(int i = 0; i < N; i++){for(int j = 0; j < N; j++)state[i][j] = (1 << N) - 1;}int cnt = 0;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(str[i][j] != '-') draw(i, j, str[i][j] - 'A');else cnt++;}}dfs(cnt);for(int i = 0; i < N; i++){for(int j = 0; j < N; j++) out.print(str[i][j]);out.println();}out.println();out.flush();if(!stringTokenizer.hasMoreTokens()) break;}} }
-
题目描述:若序列满足以下要求,x[1] = 1, x[m] = n ,x[1] < x[2] < … x[m - 1] < x[m],对于每个k(1 <= k <= m)都存在两个整数i和j(1 <= i, j <= k - 1, i和j可相等)使得x[k] = x[i] + x[j]则被称为加成序列。现在给定一个整数n,找出符合上述条件的长度m最小的加成序列。
**数据规模:**1 <= n <= 100
**分析:**这里要找出符合上述条件的长度位m最小的加成序列。这里我们可以采用迭代加深的思想进行搜索。
优化:
- 迭代加深进行深度搜索
- 我们用path存储序列
- 去重,避免重复搜索
题解: 见分析
TIPS:
时间复杂度:O(2^20)
#include<iostream>using namespace std;const int N = 110; int n; int path[N];bool dfs(int u, int depth){if(u == depth) return path[u - 1] == n;bool st[N] = {false};for(int i = u - 1; i >= 0; i--){for(int j = i; j >= 0; j--){int s = path[i] + path[j];//1.保证递增. 2.保证小于或等于n. 3.保证未选用于去重if(s >= path[u - 1] && s <= n && !st[s]){st[s] = true;path[u] = s;if(dfs(u + 1, depth)) return true;}}}return false; } int main(){while(cin >> n, n){int depth = 1;path[0] = 1;while(!dfs(1, depth)) depth++;for(int i = 0; i < depth; i++) cout << path[i] << ' ';cout << endl;}return 0; }
import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 110;int n;int[] path = new int[N];/*** 搜索* @param u 存储当前下标* @param depth 要搜索的深度* @return*/boolean dfs(int u, int depth){if(u == depth) return path[u - 1] == n;boolean[] st = new boolean[N];for(int i = u - 1; i >= 0; i--){for(int j = i; j >= 0; j--){int s = path[i] + path[j];if(s >= path[u - 1] && s <= n && !st[s]){st[s] = true;path[u] = s;if(dfs(u + 1, depth)) return true;}}}return false;}void run(){Scanner sc = new Scanner(System.in);while(true){n = sc.nextInt();if(n == 0) break;int depth = 1;path[0] = 1;while(!dfs(1, depth)) depth++;for(int i = 0; i < depth; i++) System.out.print(path[i] + " ");System.out.println();}sc.close();} }
-
题目描述:有N个物品,其中第i个的重量是g[i]。一次可以拿的物品重量不超过w。现在想知道在不超过w的情况下一次能搬动的最大重量是多少?
**数据规模:**1 <= n <= 46, 1 <= w,g[i] <= 2^31 - 1
**分析:**这里可以采用双向dfs搜索,将时间复杂度大幅度降低,体现了以空间换时间的思想。
- 先搜索前N/2个物品可以凑出来的所有重量,存到数组当中去。
- 对所有重量排序,判重。
- 再搜索后一半物品可以凑出来的重量。假设当前的重量是x,则可以在预处理出的所有重量中二分出一个y,使得 x + y <= w,且x + y最大。
优化:
- 从大到小枚举所有质量。
- 均衡两次搜索的时间。
题解: 见分析
TIPS:
时间复杂度:O(2^24)
#include<iostream> #include<algorithm>using namespace std; typedef long long LL; const int N = 46;int n, m, k; LL g[N]; LL weight[1 << 24], cnt; LL ans = 0; /**** @param u 当前层* @param s 当前重量*/ void dfs_1(int u, int s){if(u == k){weight[cnt++] = s;return;}if(s + g[u] <= m) dfs_1(u + 1, s + g[u]);dfs_1(u + 1, s); }void dfs_2(int u, int s){if(u == n){int l = 0, r = cnt - 1;while(l < r){int mid = l + r + 1 >> 1;if(weight[mid] + s <= m) l = mid;else r = mid - 1;}if(weight[l] + s <= m) ans = max(ans, weight[l] + s);return;}if(s + g[u] <= m) dfs_2(u + 1, s + g[u]);dfs_2(u + 1, s); }int main(){cin >> m >> n;for(int i = 0; i < n; i++) cin >> g[i];sort(g, g + n, greater<int>());k = n / 2 + 2;dfs_1(0, 0);sort(weight, weight + cnt);cnt = unique(weight, weight + cnt) - weight;dfs_2(k, 0);cout << ans << endl;return 0; }
import java.util.Arrays; import java.util.Scanner; import java.util.TreeSet;public class Main {public static void main(String[] args) {new Main().run();}final int N = 46;int n, k, m; //分别代表的个数,搜索的分割点和可移动的最大质量Integer[] g = new Integer[N];//存放所有礼物的质量int[] weight = new int[1 << 24];//存放枚举的质量之和int cnt = 0;//质量之和的下标long ans = 0;//当前合法最大质量TreeSet<Integer> set = new TreeSet<>();//有序集合用于存放去重之后的质量之和按升序排序/*** 第一次搜索* @param u 当前位置* @param s 当前质量之和*/void dfs1(int u, int s){//边界条件:若下标等于分界点,就将当前s存入weight中并返回if(u == k) {weight[cnt++] = s;return;}//分割条件:优先选当前数,若结果之和小于mif((long) g[u] + s <= m) dfs1(u + 1, g[u] + s);//不选当前数dfs1(u + 1, s);}void dfs2(int u, int s){//边界条件: 若当前下标位结尾,就在有序集合中找一个最大的小于或等于目标数m - 当前数s的数,并用该数更新ans,最后返回if(u == n){int t = set.floor(m - s);ans = Math.max(ans, s + t);return;}//分割:选当前数或不选,优先选if((long) s + g[u] <= m) dfs2(u + 1, s + g[u]);dfs2(u + 1, s);}void run(){Scanner sc = new Scanner(System.in);m = sc.nextInt();n = sc.nextInt();for (int i = 0; i < n; i++) {g[i] = sc.nextInt();}sc.close();Arrays.sort(g, 0, n, (a, b) -> b - a);k = n / 2 + 1;dfs1(0, 0);for(int i = 0; i < cnt; i++) set.add(weight[i]);dfs2(k, 0);System.out.println(ans);} }
-
题目描述:立体推箱子,我们给定箱子的初始位置以及初始状态,我们想让箱子到目标位置。箱子可以往前后左右滚动,每次滚动只能转动90度,现在想知道从起始位置到终止位置最少需要多少步?
**数据规模:**3 <= N, M <= 500
**分析:**从起点想知道到终点最少需要多少步,可以用宽搜来做。因为每个位置对应有三种状态,竖直、横着、竖着。三种状态,三种状态。这类题目对于状态的转移很重要。我们将状态存入Node中,有三个属性x、y、lie表示当前长方体的位置以及摆放的状态。dist中存放的是不同状态离初始位置的距离。
**优化:**无
题解: 见分析
TIPS:无
时间复杂度:O(N^2 * 3)
#include<iostream> #include<cstring> #include<queue> #include<algorithm>using namespace std; const int N = 510; struct State{int x, y, lie; }; char g[N][N]; int dis[N][N][3]; int f[3][4][3] = {{{-2, 0, 2}, {1, 0, 2}, {0, -2, 1}, {0, 1, 1}},{{-1, 0, 1}, {1, 0, 1}, {0, -1, 0}, {0, 2, 0}},{{-1, 0, 0}, {2, 0, 0}, {0, -1, 2}, {0, 1, 2}} }; int n, m;bool check(int x, int y){if(x < 0 || x >= n || y < 0 || y >= m) return false;return g[x][y] != '#'; } int bfs(State start, State end){memset(dis, -1, sizeof dis);dis[start.x][start.y][start.lie] = 0;queue<State> queue;queue.push(start);while(queue.size()){auto t = queue.front();queue.pop();for(int i = 0; i < 4; i++){State next = {t.x + f[t.lie][i][0], t.y + f[t.lie][i][1], f[t.lie][i][2]};int x = next.x, y = next.y;if(!check(x, y)) continue;if(next.lie == 0 && g[x][y] == 'E') continue;if(next.lie == 1 && !check(x, y + 1)) continue;if(next.lie == 2 && !check(x + 1, y)) continue;if(dis[x][y][next.lie] == -1){dis[x][y][next.lie] = dis[t.x][t.y][t.lie] + 1;queue.push(next);}}}return dis[end.x][end.y][end.lie]; } int main(){while(cin >> n >> m, n || m){for(int i = 0; i < n; i++) cin >> g[i];State start = {-1}, end;for(int i = 0; i < n; i++){for(int j = 0; j < m; j++){if(g[i][j] == 'X' && start.x == -1){if(g[i][j + 1] == 'X') start = {i, j, 1};else if(g[i + 1][j] == 'X') start = {i, j, 2};else start = {i, j, 0};}else if(g[i][j] == 'O') end = {i, j, 0};}}int ans = bfs(start, end);if(ans == -1) puts("Impossible");else printf("%d\n", ans);}return 0;}
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*;public class Main {public static void main(String[] args) {new Main().run();}final int N = 510;/*** x、y分别表示横坐标和纵坐标,lie表示长方体的拜访状态。0-直立、1-横着、2-竖着*/class Node{int x, y, lie;public Node(int x, int y, int lie) {this.x = x;this.y = y;this.lie = lie;}@Overridepublic String toString() {return "Node{" +"x=" + x +", y=" + y +", lie=" + lie +'}';}}int[][][] f = {{{-2, 0, 2}, {1, 0, 2}, {0, -2, 1}, {0, 1, 1}},{{-1, 0, 1}, {1, 0, 1}, {0, -1, 0}, {0, 2, 0}},{{-1, 0, 0}, {2, 0, 0}, {0, -1, 2}, {0, 1, 2}}};char[][] g = new char[N][N];//存地图int[][][] dist = new int[N][N][3];//存放当前状态与原点的距离,初始为-1代表未走过int n, m;boolean check(int x, int y){if(x < 0 || x >= n || y < 0 || y >= m) return false;return g[x][y] != '#';}/*** 广度优先搜索* @param start* @param end* @return*/int bfs(Node start, Node end){for (int i = 0; i < n; i++) {for(int j = 0; j < m; j++){Arrays.fill(dist[i][j], -1);}}dist[start.x][start.y][start.lie] = 0;Queue<Node> queue = new ArrayDeque<>();queue.offer(start);while(!queue.isEmpty()){Node cur = queue.poll();for (int i = 0; i < 4; i++) {Node next = new Node(cur.x + f[cur.lie][i][0], cur.y + f[cur.lie][i][1], f[cur.lie][i][2]);int x = next.x, y = next.y;if(!check(x, y)) continue;if(next.lie == 0 && g[x][y] == 'E') continue;//立着if(next.lie == 1 && !check(x, y + 1)) continue;//横着if(next.lie == 2 && !check(x + 1, y)) continue;//竖着if(dist[x][y][next.lie] == -1){//未访问dist[x][y][next.lie] = dist[cur.x][cur.y][cur.lie] + 1;//步数+1queue.offer(next);}}}return dist[end.x][end.y][end.lie];}StringTokenizer stringTokenizer;BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));String next(){while(stringTokenizer == null || !stringTokenizer.hasMoreTokens()){try {stringTokenizer = new StringTokenizer(bufferedReader.readLine());} catch (IOException e) {e.printStackTrace();}}return stringTokenizer.nextToken();}int nextInt(){return Integer.parseInt(next());}void run(){while(true){n = nextInt();m = nextInt();if(n == 0 && m == 0) break;for(int i = 0; i < n; i++) {g[i] = next().toCharArray();}Node start = new Node(-1, -1, -1), end = new Node(-1, -1, -1);for(int i = 0; i < n; i++){for(int j = 0; j < m; j++){if(g[i][j] == 'X' && start.x == -1){//这里要保证是第一个if(g[i][j + 1] == 'X'){start = new Node(i, j, 1);}else if(g[i + 1][j] == 'X'){start = new Node(i, j, 2);}else{start = new Node(i, j, 0);}}else if(g[i][j] == 'O'){end = new Node(i, j, 0);}}}int ans = bfs(start, end);if(ans == -1){System.out.println("Impossible");}else System.out.println(ans);}} }
-
题目描述:给定一个01矩阵,定义曼哈顿距离-即两点的纵坐标之差与横坐标只差的和
d i s t ( A [ i ] [ j ] , A [ k ] [ l ] ) = ∣ i − k ∣ + [ j − l ] dist(A[i][j], A[k][l]) = |i - k| + [j - l] dist(A[i][j],A[k][l])=∣i−k∣+[j−l]
输出一个N行M列的整数矩阵,表示当前位置到最近1的距离
B [ i ] [ j ] = m i n 1 ≤ x ≤ N , 1 ≤ y ≤ M , A [ x ] [ y ] = 1 d i s t ( A [ i ] [ j ] , A [ x ] [ y ] ) B[i][j] = min_{1 \leq x \leq N, 1\leq y \leq M, A[x][y] = 1} dist(A[i][j], A[x][y]) B[i][j]=min1≤x≤N,1≤y≤M,A[x][y]=1dist(A[i][j],A[x][y])
**数据规模:**1 <= N <= 1000**分析:**这里我们可以用多源宽搜来做,等价于虚拟的单源宽搜。我们可以先将所有为1的点加入队列中,并将该点的距离标记为0。然后进行宽搜。每次搜索到一个合法的位置我们就更新当前点的距离
d i s [ n x ] [ n y ] = d i s [ x ] [ y ] + 1 dis[nx][ny] = dis[x][y] + 1 dis[nx][ny]=dis[x][y]+1
**优化:**无题解: 见分析
TIPS:无
时间复杂度:O(N^2)
#include<cstring> #include<iostream> #include<algorithm> #include<queue>using namespace std; typedef pair<int, int> PII; const int N = 1010;int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1}; char g[N][N]; int dis[N][N]; int n, m;bool check(int x, int y){if(x < 0 || x >= n || y < 0 || y >= m) return false;return dis[x][y] == -1; } void bfs(){memset(dis, -1, sizeof dis);queue<PII> q;for(int i = 0; i < n; i++){for(int j = 0; j < m; j++){if(g[i][j] == '1'){q.push({i, j});dis[i][j] = 0;}}}while(q.size()){auto t = q.front();q.pop();int x = t.first, y = t.second;for(int i = 0; i < 4; i++){int nx = x + dx[i], ny = y + dy[i];if(check(nx, ny)){dis[nx][ny] = dis[x][y] + 1;q.push({nx, ny});}}} }int main(){cin >> n >> m;for(int i = 0; i < n; i++) scanf("%s", g[i]);bfs();for(int i = 0; i < n; i++){for(int j = 0; j < m; j++) printf("%d ", dis[i][j]);puts("");}return 0; }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Queue; import java.util.StringTokenizer;public class Main {public static void main(String[] args) {new Main().run();}final int N = 1010;class Pair{int x, y;public Pair(int x, int y) {this.x = x;this.y = y;}}char[][] g = new char[N][N];int[][] dis = new int[N][N];int[] dx = {-1, 1, 0, 0}, dy = {0, 0, -1, 1};int n, m;boolean check(int x, int y){if(x < 0 || x >= n || y < 0 || y >= m) return false;return dis[x][y] == -1;}StringTokenizer stringTokenizer;BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));String next(){while(stringTokenizer == null || !stringTokenizer.hasMoreElements()){try {stringTokenizer = new StringTokenizer(bufferedReader.readLine());} catch (IOException e) {e.printStackTrace();}}return stringTokenizer.nextToken();}int nextInt(){return Integer.parseInt(next());}/*** 用于计算当前位置到1的最短距离*/void bfs(){for(int i = 0; i < n; i++) Arrays.fill(dis[i], -1);Queue<Pair> queue = new ArrayDeque<>();for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if(g[i][j] == '1'){dis[i][j] = 0;queue.offer(new Pair(i, j));}}}while(!queue.isEmpty()){Pair t = queue.poll();int x = t.x, y = t.y;for(int i = 0; i < 4; i++){int nx = x + dx[i], ny = y + dy[i];if(check(nx, ny)){dis[nx][ny] = dis[x][y] + 1;queue.offer(new Pair(nx, ny));}}}}void run(){PrintWriter out = new PrintWriter(System.out);n = nextInt();m = nextInt();for(int i = 0; i < n; i++) {char[] str = next().toCharArray();for(int j = 0; j < m; j++) {g[i][j] = str[j];}}bfs();for(int i = 0; i < n; i++){for(int j = 0; j < m; j++) out.print(dis[i][j] + " ");out.println();}out.flush();out.close();} }
-
题目描述:> 推箱子,你将控制一个人把一个箱子推到目的地。给定一张n行m列的地图用
.
表示空地,字符#
表示墙,字符S
表示人的初始位置,字符B
表示箱子的起始位置,字符T
表示箱子的目标位置。求一种方案,使得箱子的移动次数最少,在此基础上再让人移动的总步数最少。方案中使用大写的EWSN
表示箱子的移动,使用小写的ewsn
表示人的移动。**数据规模:**1 <= N <= 20
分析:
- 箱子移动的次数最少
- 人的移动次数最少
- 移动箱子的操作序列的字典序最小
- 移动人的操作序列的字典序最小
**优化:**无
题解: 见分析
TIPS:无
时间复杂度:
O(MN)^2
#include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<queue> using namespace std; typedef pair<int, int> PII; const int N = 25; int dx[4] = {1, -1, 0, 0};//上下左右, int dy[4] = {0, 0, 1, -1}; char ops[5] = "nswe";struct Point{int x, y, dir; };int n, m; char g[N][N]; PII dist[N][N][4]; //存到每个状态的最短距离,first存箱子的移动距离,second存人的移动距离 Point pre[N][N][4];//存箱子从哪个状态转移过来的 vector<int> path[N][N][4];//存到每个状态的路径 bool st[N][N][4], vis[N][N];//对箱子做bfs时用st判重,对人做bfs时用vis判重 int preman[N][N];//存人是从哪个状态转移过来的bool check(int x, int y){return ~x && ~y && x < n && y < m && g[x][y] != '#'; } /*** 对人做宽搜。判断人是否能从start走到end且不经过box,若能将路景观存入seq中,并返回长度,若不能返回-1* @param start* @param end* @param box* @param seq* @return*/ int bfs_man(PII start, PII end, PII box, vector<int> &seq){//初始化vis和premanmemset(vis,false, sizeof vis);memset(preman, -1, sizeof preman);vis[start.first][start.second] = true;//start位置不能走vis[box.first][box.second] = true;//box的位置不能走queue<PII> q;q.push(start);while(q.size()){auto t = q.front();q.pop();int x = t.first, y = t.second;//如果走到终点if(t == end){seq.clear();while(~preman[x][y]){int dir = preman[x][y];seq.push_back(dir);x += dx[dir];y += dy[dir];//x和y按相反的方向走回去}return seq.size();//返回路径的长度}for(int i = 0; i < 4; i++){//由于dx和dy存的是人对箱子的方向,和所要走的四个方向是反着的//直接用 + dx[i] 和 + dy[i]也能遍历四个方向,但是无法保证字典序最小//所以这里要反过来用, -dx[i], -dy[i]int a = x - dx[i], b = y - dy[i];if(!check(a, b) || vis[a][b]) continue;//若(a, b)不能走,就跳过vis[a][b] = true;//标记preman[a][b] = i;//记录方向q.push({a, b}); //入队}}return -1;//没有就返回-1 } /*** 对箱子做宽搜,并返回是否能到终点* @param start* @param box* @param end* @return*/ bool bfs_box(PII start, PII box, Point &end){memset(st, false, sizeof st);queue<Point> q;//枚举位置for(int i = 0; i < 4; i++){int a = box.first + dx[i], b = box.second + dy[i];//人要走的位置int c = box.first - dx[i], d = box.second - dy[i];//箱子推到的位置if(!check(a, b) || !check(c, d)) continue; //若不合法就跳过vector<int> seq;int len = bfs_man(start, {a, b}, box, seq);//从start到(a, b)且不经过box,将路径存入seq中if(len == -1) continue;q.push({c, d, i});st[c][d][i] = true;//标记path[c][d][i] = seq;//到该点的路径为seqdist[c][d][i] = {1, len};//到该点的距离为: 箱子被推了1步,人走了len步//记录该点是从{box.first, box.second}到达的,为了方便,将方向置为-1pre[c][d][i] = {box.first, box.second, -1};}PII maxd = {1e9, 1e9};// 存到终点的距离while(q.size()){auto t = q.front();q.pop();PII &v = dist[t.x][t.y][t.dir];//取出队头状态的距离//如果达到了终点,并且距离比maxd更小,那么更行if(g[t.x][t.y] == 'T' && v < maxd){end = t;maxd = v;}for(int i = 0; i < 4; i++){int a = t.x + dx[i], b = t.y + dy[i];//人要走的位置int c = t.x - dx[i], d = t.y - dy[i];//箱子要走的位置if(!check(a, b) || !check(c, d)) continue;vector<int> seq;//箱子是从t.dir的方向被推过来的,所有推之前的位置就是(t.x + dx[t.dir], t.y + dy[t.dir])//所以推完之后人的位置就在(t.x + dx[t.dir], ty + dy[t.dir])int len = bfs_man({t.x + dx[t.dir], t.y + dy[t.dir]}, {a, b}, {t.x, t.y}, seq);if(len == -1) continue;PII distance = {v.first + 1, v.second + len};//能到这个状态就更新if(!st[c][d][i]){//若当前状态未到过q.push({c, d, i});//入队st[c][d][i] = true;path[c][d][i] = seq;dist[c][d][i] = distance;pre[c][d][i] = t;}else if(dist[c][d][i] > distance){//若此次距离更近dist[c][d][i] = distance;path[c][d][i] = seq;pre[c][d][i] = t;}}}return maxd.first != 1e9;//更新过 } int main(){int T = 1;while(cin >> n >> m, n || m){printf("Maze #%d\n", T++);//读入地图for(int i = 0; i < n; i++) cin >> g[i];PII start, box;//找到人的位置和箱子的位置,存入start和box中for(int i = 0; i < n; i++){for(int j = 0; j < m; j++){if(g[i][j] == 'S') start = {i, j};else if(g[i][j] == 'B') box = {i, j};}}Point end; string ans;//end存终点的位置,ans存答案,这里end在bfs中记录if(!bfs_box(start, box, end)) ans = "Impossible.";else{while(~end.dir){ans += ops[end.dir] - 32;//将小写字母转为大写字母for(int i = 0; i < path[end.x][end.y][end.dir].size(); i++){ans += ops[path[end.x][end.y][end.dir][i]];//存入移动的路径}end = pre[end.x][end.y][end.dir]; //end往前倒着找}reverse(ans.begin(), ans.end());//由于记录的序列是倒着的,这里需要翻转}cout << ans << endl << endl;}return 0; }
import java.util.*;public class Main {public static void main(String[] args) {new Main().run();}final int N = 25;int[] dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};//上下左右char[] ops = {'n', 's', 'w', 'e'};class Point{int x, y, dir;//当前点的坐标以及从哪个方向转移过来public Point(int x, int y, int dir) {this.x = x;this.y = y;this.dir = dir;}@Overridepublic String toString() {return "Point{" +"x=" + x +", y=" + y +", dir=" + dir +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Point point = (Point) o;return x == point.x &&y == point.y &&dir == point.dir;}@Overridepublic int hashCode() {return Objects.hash(x, y, dir);}}class Pair{int f, s;public Pair(int f, int s) {this.f = f;this.s = s;}@Overridepublic String toString() {return "Pair{" +"f=" + f +", s=" + s +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Pair pair = (Pair) o;return f == pair.f &&s == pair.s;}@Overridepublic int hashCode() {return Objects.hash(f, s);}}int n, m;char[][] g = new char[N][];Pair[][][] dist = new Pair[N][N][4]; //存到每个状态的最短距离,first存存箱子的移动距离,second存人的移动距离Point[][][] pre = new Point[N][N][4];//存箱子从哪个状态转移过来List<Integer>[][][] path = new List[N][N][4];//存到每个状态的路径boolean[][][] st = new boolean[N][N][4];//存对箱子搜索时进行判重boolean[][] vis = new boolean[N][N];//存对人搜索时判重int[][] preman = new int[N][N];//存人是从哪个状态转移过来的/*** 判断当前位置是否可以走* @param x* @param y* @return*/boolean check(int x, int y){if(x < 0 || x >= n || y < 0 || y >= m) return false;return g[x][y] != '#';}/*** 前面一个比后面一个小* @param a* @param b* @return*/boolean compare(Pair a, Pair b){if(a.f < b.f) return true;if(a.f == b.f && a.s < b.s) return true;return false;}List<Integer> seq = new ArrayList<>();/*** 搜索人从起点到终点不经过box的最短步数,这里若可以到达终点将路线存入seq中* @param start* @param end* @param box* @return 返回步数*/int bfsMan(Pair start, Pair end, Pair box){for(int i = 0; i < n; i++) {Arrays.fill(vis[i], false);Arrays.fill(preman[i], -1);}vis[start.f][start.s] = true;vis[box.f][box.s] = true;Queue<Pair> q = new ArrayDeque<>();q.offer(start);while(!q.isEmpty()){Pair t = q.poll();//System.out.println(t);int x = t.f, y = t.s;//若走到终点if(t.equals(end)){seq.clear();while(preman[x][y] >= 0){int dir = preman[x][y];seq.add(dir);//往相仿方向走回去x += dx[dir];y += dy[dir];}return seq.size();}for(int i = 0; i < 4; i++){//这里dx和dy是存的人对箱子的方向int a = x - dx[i], b = y - dy[i];//System.out.println("a : " + a + " b " + b);//if(a == 0 && b == 1) System.out.println("vis : " + vis[0][1]);if(!check(a, b) || vis[a][b]) continue;;vis[a][b] = true;preman[a][b] = i;q.add(new Pair(a, b));}}return -1;}Point end;/*** 对箱子进行宽搜返回是否能到终点* @param start* @param box* @return*/boolean bfsBox(Pair start, Pair box){for (int i = 0; i < n; i++) {for(int j = 0; j < m; j++) Arrays.fill(st[i][j], false);}Queue<Point> q = new ArrayDeque<>();//枚举位置for(int i = 0; i < 4; i++){int a = box.f + dx[i], b = box.s + dy[i];//人要走的位置int c = box.f - dx[i], d = box.s - dy[i];//箱子要推到的位置if(!check(a, b) || !check(c, d)) continue;seq.clear();int len = bfsMan(start, new Pair(a, b), box);if(len == -1) continue;q.offer(new Point(c, d, i));st[c][d][i] = true;path[c][d][i] = new ArrayList<>(seq);dist[c][d][i] = new Pair(1, len);//箱子被推了一步,人走了len步pre[c][d][i] = new Point(box.f, box.s, -1);}Pair maxd = new Pair((int)1e9, (int)1e9);while(!q.isEmpty()){Point t = q.poll();Pair v = dist[t.x][t.y][t.dir];if(g[t.x][t.y] == 'T' && compare(v, maxd)){end = new Point(t.x, t.y, t.dir);maxd = new Pair(v.f, v.s);}for(int i = 0; i < 4; i++){int a = t.x + dx[i], b = t.y + dy[i];//人要走的位置int c = t.x - dx[i], d = t.y - dy[i];//箱子要走的位置if(!check(a, b) || !check(c, d)) continue;;seq.clear();int len = bfsMan(new Pair(t.x + dx[t.dir], t.y + dy[t.dir]), new Pair(a, b), new Pair(t.x, t.y));if(len == -1) continue;Pair distance = new Pair(v.f + 1, v.s + len);if(!st[c][d][i]){q.offer(new Point(c, d, i));st[c][d][i] = true;path[c][d][i] = new ArrayList<>(seq);dist[c][d][i] = new Pair(distance.f, distance.s);pre[c][d][i] = t;}else if(compare(distance, dist[c][d][i])){dist[c][d][i] = new Pair(distance.f, distance.s);path[c][d][i] = new ArrayList<>(seq);pre[c][d][i] = new Point(t.x, t.y, t.dir);}}}return maxd.f != (int) 1e9;}void run(){Scanner sc = new Scanner(System.in);int T = 1;while(true){n = sc.nextInt();m = sc.nextInt();if(n == 0 && m == 0) break;System.out.printf("Maze #%d\n", T++);//写入地图for(int i = 0; i < n; i++){g[i] = sc.next().toCharArray();}//记录起点和箱子位置Pair start = new Pair(-1, -1), box = new Pair(-1, -1);for(int i = 0; i < n; i++){for(int j = 0; j < m; j++){if(g[i][j] == 'S') start = new Pair(i, j);if(g[i][j] == 'B') box = new Pair(i, j);}}end = new Point(-1, -1, -1);String ans;if(!bfsBox(start, box)) ans = "Impossible.";else{StringBuilder stringBuilder = new StringBuilder();while(end.dir >= 0){stringBuilder.append((char)(ops[end.dir] - 32));//将大写字母转化为小写字母for(int i = 0; i < path[end.x][end.y][end.dir].size(); i++){stringBuilder.append(ops[path[end.x][end.y][end.dir].get(i)]);//存入路径}end = pre[end.x][end.y][end.dir];}stringBuilder.reverse();ans = stringBuilder.toString();}System.out.println(ans + "\n");}sc.close();} }
-
题目描述:给定一个图,图中路径有两种表示\或/,改变路径所花费的体力为1, 不改变花费体力为0,现在想从(0, 0)到达终点(n, m),求最小的体力消耗。
**数据规模:**1 <= N <= 500
**分析:**典型的图论问题,这里使用优化版的
Dijkstra
算法来实现优化:
- 我们使用双端队列,当要去的点的坐标和图中路径的方向相同时我们将要去的点加入队头,否则加入队尾。这样就保障了队列是路径长度是单调递增的。
- 使用标记数组标记当前点是否访问过,若已经访问过则跳过该点。
- 若当前点的坐标为终点,直接返回该点的路径。
题解: 见分析
TIPS:
- 这里
dx
、dy
存储的是当前点到要去的点的偏移量。即点和点的关系。 ix
、iy
存储的是当前点和要去点的之间隔着的方块坐标,就是当前点与方块坐标的偏移量。即点和方块的关系。
时间复杂度:
O(m*n)
#include<iostream> #include<cstring> #include<deque>using namespace std; typedef pair<int, int> PII; const int N = 510;int n, m; char g[N][N]; int dis[N][N]; int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, 1, -1}; int ix[4] = {-1, -1, 0, 0}, iy[4] = {-1, 0, 0, -1}; char cs[] = "\\/\\/"; int bfs(PII p){memset(dis, 0x3f, sizeof dis);deque<PII> dq;dis[0][0] = 0;dq.push_back(p);while(dq.size()){auto t = dq.front();dq.pop_front();int x = t.first, y = t.second;for(int i = 0; i < 4; i++){int a = x + dx[i], b = y + dy[i];if(a >= 0 && a <= n && b >= 0 && b <= m){int w = 0;int c = x + ix[i], d = y + iy[i];if(g[c][d] != cs[i]) w = 1;if(dis[a][b] > dis[x][y] + w){dis[a][b] = dis[x][y] + w;if(w) dq.push_back({a, b});else dq.push_front({a, b});}}}}return dis[n][m] == 0x3f3f3f3f ? -1 : dis[n][m]; } int main(){int T;scanf("%d", &T);while(T--){cin >> n >> m;for(int i = 0; i < n; i++) scanf("%s", g[i]);int ans = bfs({0, 0});if(ans == -1) puts("NO SOLUTION");else printf("%d\n", ans);}return 0; }
import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 510, INF = (int) 1e9;char[][] g = new char[N][];boolean[][] st = new boolean[N][N];char[] str = {'\\', '/', '\\', '/'};int[][] dis = new int[N][N];int[] dx = {-1, -1, 1, 1}, dy = {-1, 1, 1, -1};//存点和点的关系int[] ix = {-1, -1, 0, 0}, iy = {-1, 0, 0, -1};//存点和格子的关系class Pair{int x, y;public Pair(int x, int y) {this.x = x;this.y = y;}}int n, m;int bfs(){for (int i = 0; i <= n; i++) {Arrays.fill(st[i], false);Arrays.fill(dis[i], INF);}Deque<Pair> deque = new ArrayDeque<>();dis[0][0] = 0;deque.offer(new Pair(0, 0));while(!deque.isEmpty()){Pair t = deque.poll();int x = t.x, y = t.y;if(st[x][y]) continue;if(x == n && y == m) return dis[x][y];st[x][y] = true;for(int i = 0; i < 4; i++){int a = x + dx[i], b = y + dy[i];if(a >= 0 && a <= n && b >= 0 && b <= m){int w = 0;int c = x + ix[i], d = y + iy[i];//用当前点的坐标计算出对应格子的坐标if(g[c][d] != str[i]) w = 1;//若相同则不必旋转,边权位0,反之为1//dijkstra算法if(dis[a][b] > dis[x][y] + w){dis[a][b] = dis[x][y] + w;//根据权值加入队头或队尾if(w > 0) deque.offer(new Pair(a, b));else deque.offerFirst(new Pair(a, b));}}}}return dis[n][m] == INF ? -1 : dis[n][m];}void run(){Scanner sc = new Scanner(System.in);int T = sc.nextInt();while(T-- > 0){n = sc.nextInt();m = sc.nextInt();for (int i = 0; i < n; i++) {g[i] = sc.next().toCharArray();}int t = bfs();if(t == -1) System.out.println("NO SOLUTION");else System.out.println(t);}sc.close();} }
-
题目描述:有N个城市(编号0、1…N-1) 和 M 条道路,构成一张无向图。每个城市里有一个加油站,不同加油站的单位油价可能不一样。现在给定k个询问,每个询问问你一辆油箱容量为C的汽车,从
a
到b
城市最少的花费是多少?(起始时汽车没有油)**数据规模:**1 <= N <= 1000, 1 <= M <= 10000, 1 <= C <= 100
**分析:**典型的图论问题,这里使用
Dijkstra
算法来实现**优化:**无
题解: 该问题中每个点可能有不同的油量。在求最短路问题中,可以将一个点拆分成多个点。这里我们将不同的容量拆成不同的点。
- 当前点可以加油时,且加油后能使得总成本更小就就加油并更新dist
- 当前点可以到达其它点且能使得总成本更小就更新dist
TIPS:无
时间复杂度:
O((n+m)*logn)
#include<iostream> #include<cstring> #include<queue>using namespace std; const int N = 1010, M = 20010, C = 110; struct Point{int d, u, c;//分别存油钱、点的编号、剩余油量/*** 重载小于号,按大于号反过来重载小于号,后面建堆时建大顶堆* @param t* @return*/bool operator < (const Point &t) const{return d > t.d;} }; int n, m; int price[N]; int h[N], e[M], w[M], ne[M], idx; int dist[N][C]; bool st[N][C];void add(int a, int b, int c){e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; }int dijkstra(int c, int start, int end){priority_queue<Point> heap;memset(dist, 0x3f, sizeof dist);memset(st, false, sizeof st);heap.push({0, start, 0});dist[start][0] = 0;while(heap.size()){auto t = heap.top();heap.pop();if(t.u == end) return t.d;if(st[t.u][t.c]) continue;st[t.u][t.c] = true;if(t.c < c){//可以加油if(dist[t.u][t.c + 1] > t.d + price[t.u]){//若这次加油后油钱比之前低dist[t.u][t.c + 1] = t.d + price[t.u];heap.push({dist[t.u][t.c + 1], t.u, t.c + 1});}}for(int i = h[t.u]; ~i; i = ne[i]){//遍历所有边if(t.c >= w[i]){//若当前剩余油量可以到if(dist[e[i]][t.c - w[i]] > t.d){//若这次到该点的油钱小于原来就更新dist[e[i]][t.c - w[i]] = t.d;heap.push({t.d, e[i], t.c - w[i]});}}}}return -1; } int main(){cin >> n >> m;for(int i = 0; i < n; i++) scanf("%d", &price[i]);memset(h, -1, sizeof h);while(m--){int a, b, c;scanf("%d%d%d", &a, &b, &c);add(a, b, c);add(b, a, c);}int query;scanf("%d", &query);while(query--){int c, s, e;scanf("%d%d%d", &c, &s, &e);int t = dijkstra(c, s, e);if(t == -1) puts("impossible");else printf("%d\n", t);}return 0; }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.StreamTokenizer; import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue;public class Main {public static void main(String[] args) {new Main().run();}final int N = 1010, M = 20010, C = 110, INF = 0x3f3f3f3f;class Point{int d, u, c;//分别表示当前油钱、点编号、剩余油public Point(int s, int u, int c) {this.d = s;this.u = u;this.c = c;}}int n, m;int[] price = new int[N];int[] h = new int[N], e = new int[M], ne = new int[M], w = new int[M];int idx = 0;int[][] dist = new int[N][C];boolean[][] st = new boolean[N][C];void add(int a, int b, int c){e[idx] = b;w[idx] = c;ne[idx] = h[a];h[a] = idx++;}StreamTokenizer streamTokenizer = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));int nextInt(){try {streamTokenizer.nextToken();} catch (IOException ioException) {ioException.printStackTrace();}return (int) streamTokenizer.nval;}int bfs(int c, int start, int end){PriorityQueue<Point> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a.d));//默认for(int i = 0; i < n; i++){Arrays.fill(dist[i], INF);Arrays.fill(st[i], false);}heap.add(new Point(0, start, 0));dist[start][0] = 0;while(!heap.isEmpty()){Point t = heap.poll();//特判if(t.u == end) return t.d;//当已经到达终点if(st[t.u][t.c]) continue;//当前点已访问就跳过st[t.u][t.c] = true;//当该点可以加油且可以使得成本更小if(t.c < c){if(dist[t.u][t.c + 1] > t.d + price[t.u]){dist[t.u][t.c + 1] = t.d + price[t.u];heap.add(new Point(dist[t.u][t.c + 1], t.u, t.c + 1));}}//当该点的边可以到达且可以使得成本更小for(int i = h[t.u]; i != -1; i = ne[i]){if(t.c >= w[i]){if(dist[e[i]][t.c - w[i]] > t.d){dist[e[i]][t.c - w[i]] = t.d;heap.add(new Point(t.d, e[i], t.c - w[i]));}}}}return -1;}void run(){n = nextInt();m = nextInt();for(int i = 0; i < n; i++) price[i] = nextInt();Arrays.fill(h, -1);while(m-- > 0){int a = nextInt(), b = nextInt(), c = nextInt();add(a, b, c);add(b, a, c);}int q = nextInt();while(q-- > 0){int c = nextInt(), s = nextInt(), e = nextInt();int t = bfs(c, s, e);if(t == -1) System.out.println("impossible");else System.out.println(t);}} }
-
题目描述:给定一张n * m的地图,地图中给出男孩、女孩和两个幽灵的坐标。男孩和女孩可以往上下左右四个方向移动,其中男孩每秒钟可以运动3个单位,即移动三次。女孩可以每秒钟移动一个单位,即一次。幽灵则会在一秒钟内同时向四个方向进行扩展,扩展后的范围是原位置对应位置曼哈顿距离不超过2所构成的面积。每次移动时幽灵会先移动,然后男孩和女孩才能移动。现在我们想知道在不遇到幽灵的情况下,男孩和女孩会和的最短时间是多少?
**数据规模:**1 < n, m <= 800
**分析:**两个人可以分别进行扩展,这里我们可以同时使用两个bfs即双向bfs分别进行扩展。这里判断某个点对应时刻是否在幽灵扩展的范围内,只需要用曼哈顿距离的求法即纵坐标之差的绝对值加横坐标之差的绝对值。
优化:
- 当两个队列中有一个为空时说明无处可走了,可以提前结束。
- 队列中每次弹出元素时先判断该元素是否是合法的再考虑其它操作。
题解:见分析
TIPS:无
时间复杂度:
O(T*N^2)
#include<iostream> #include<cstring> #include<queue> #include<algorithm>using namespace std; typedef pair<int, int> PII; const int N = 810; char g[N][N]; int st[N][N]; PII boy, girl, ghost[2];int n, m;const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1}; bool check(int x, int y, int step){if(x < 0 || x >= n || y < 0 || y >= m || g[x][y] == 'X') return false;for(int i = 0; i < 2; i++){if(abs(ghost[i].first - x) + abs(ghost[i].second - y) <= 2 * step) return false;}return true; } int bfs(){memset(st, 0, sizeof st);queue<PII> qb, qg;qb.push(boy), qg.push(girl);int step = 0;while(qb.size() && qg.size()){step++;for(int i = 0; i < 3; i++){for(int j = 0, len = qb.size(); j < len; j++){auto t = qb.front();qb.pop();int x = t.first, y = t.second;if(!check(x, y, step)) continue;for(int k = 0; k < 4; k++){int nx = x + dx[k], ny = y + dy[k];if(check(nx, ny, step)){if(st[nx][ny] == 2) return step;if(!st[nx][ny]){st[nx][ny] = 1;qb.push({nx, ny});}}}}}for(int i = 0; i < 1; i++){for(int j = 0, len = qg.size(); j < len; j++){auto t = qg.front();qg.pop();int x = t.first, y = t.second;if(!check(x, y, step)) continue;for(int k = 0; k < 4; k++){int nx = x + dx[k], ny = y + dy[k];if(check(nx, ny, step)){if(st[nx][ny] == 1) return step;if(!st[nx][ny]){st[nx][ny] = 2;qg.push({nx, ny});}}}}}}return -1; } int main(){int T;scanf("%d", &T);while(T--){scanf("%d%d", &n, &m);for(int i = 0; i < n; i++) scanf("%s", g[i]);int cnt = 0;for(int i = 0; i < n; i++)for(int j = 0; j < m; j++)if(g[i][j] == 'M') boy = {i, j};else if(g[i][j] == 'G') girl = {i, j};else if(g[i][j] == 'Z') ghost[cnt++] = {i, j};printf("%d\n", bfs());}return 0; }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Queue; import java.util.StringTokenizer;public class Main {public static void main(String[] args) {new Main().run();}final int N = 810;char[][] g = new char[N][];int[][] st = new int[N][N];int[] dx = {-1, 1, 0, 0}, dy = {0, 0, -1, 1};class Pair{int f, s;public Pair(int f, int s) {this.f = f;this.s = s;}}StringTokenizer stringTokenizer;BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));String next(){while(stringTokenizer == null || !stringTokenizer.hasMoreElements()){try {stringTokenizer = new StringTokenizer(bufferedReader.readLine());} catch (IOException e) {e.printStackTrace();}}return stringTokenizer.nextToken();}int nextInt(){return Integer.parseInt(next());}int n, m;Pair boy, girl, ghost[] = new Pair[2];boolean check(int x, int y, int step){if(x < 0 || x >= n || y < 0 || y >= m || g[x][y] == 'X') return false;for (int i = 0; i < 2; i++)if(Math.abs(x - ghost[i].f) + Math.abs(y - ghost[i].s) <= 2 * step) return false;return true;}int bfs(){for (int i = 0; i < n; i++) {Arrays.fill(st[i], 0);}Queue<Pair> qb = new ArrayDeque<>(), qg = new ArrayDeque<>();qb.offer(boy);qg.offer(girl);int step = 0;while(!qb.isEmpty() && !qg.isEmpty()){step++;for(int i = 0; i < 3; i++){for(int j = 0, len = qb.size(); j < len; j++){Pair t = qb.poll();int x = t.f, y = t.s;if(!check(x, y, step)) continue;for (int k = 0; k < 4; k++) {int nx = x + dx[k], ny = y + dy[k];if(check(nx, ny, step)){if(st[nx][ny] == 2) return step;if(st[nx][ny] == 0){st[nx][ny] = 1;qb.offer(new Pair(nx, ny));}}}}}for(int i = 0; i < 1; i++){for(int j = 0, len = qg.size(); j < len; j++){Pair t = qg.poll();int x = t.f, y = t.s;if(!check(x, y, step)) continue;for (int k = 0; k < 4; k++) {int nx = x + dx[k], ny = y + dy[k];if(check(nx, ny, step)){if(st[nx][ny] == 1) return step;if(st[nx][ny] == 0){st[nx][ny] = 2;qg.offer(new Pair(nx, ny));}}}}}}return -1;}void run(){int T = nextInt();while(T-- > 0){n = nextInt();m = nextInt();for(int i = 0; i < n; i++) g[i] = next().toCharArray();int cnt = 0;for(int i = 0; i < n; i++){for (int j = 0; j < m; j++) {if(g[i][j] == 'M') boy = new Pair(i, j);else if(g[i][j] == 'G') girl = new Pair(i, j);else if(g[i][j] == 'Z') ghost[cnt++] = new Pair(i, j);}}System.out.println(bfs());}} }
-
题目描述:给定一张N个点、M条边的有向图,求从起点S到终点T的第K短路长度? (路径允许重复经过点或边,这里边的权值非负)
**数据规模:**1 <= S, T, K <= N <= 1e3, 0 <= M <= 1e4
**分析:**点数非常多、边的权值非负可以使用启发式算法来优化宽搜。
优化:
- 建立一个启发式函数,来取代小根堆直接根据距离起点的距离来出队。
- 当某个点已经出队了k次就不需要再更新它了。
题解:
- 建立反图,然后在反向图上求出从T到其它所有点的最短距离,作为每个点的估价函数。
- 从S开始扩展,每次取出当前的估计值最小的点,将其能扩展到的所有点全扩展。 估计值 = 距离起点的距离 + 估价函数
- 当第K次遇到T时,就求出了从S到T的第K短路。
TIPS:无
时间复杂度:
O((n+m)*log(n+m)*k)
#include<iostream> #include<cstring> #include<queue> #include<vector> #include<algorithm>using namespace std; typedef pair<int, int> PII; struct Point{int d, t, e;bool operator > (const Point &t) const{return d > t.d;} }; const int N = 1010, M = 2e5 + 10, INF = 0x3f3f3f3f; int n, m; int h[N], rh[N], e[M], w[M], ne[M], idx; int dist[N], f[N]; int S, T, K; int st[N];void add(int *h, int a, int b, int l){e[idx] = b, w[idx] = l, ne[idx] = h[a], h[a] = idx++; }void dijkstra(){priority_queue<PII, vector<PII>, greater<PII>> heap;//建小根堆memset(dist, 0x3f, sizeof dist);//初始化距离为正无穷dist[T] = 0;//终点的距离为0heap.push({0, T});//将终点入队//当小根堆不为空时就弹出while(heap.size()){auto t = heap.top();heap.pop();int u = t.second;//若当前元素已访问过就跳过if(st[u]) continue;st[u] = 1;//标记为已访问//遍历所有边,看那些点再更新距离后离终点的距离更短了,若是就更新,并把该点入队for(int i = h[u]; ~i; i = ne[i]){int v = e[i];if(dist[v] > dist[u] + w[i]){dist[v] = dist[u] + w[i];heap.push({dist[v], v});}}}//将这些值存入f中memcpy(f, dist, sizeof f); } /*** A*算法-启发式算法* @return*/ int a_star() {priority_queue<Point, vector<Point>, greater<Point>> heap;//新建一个小根堆//小根堆的元素结构是{当前点距离终点的距离, 当前点距离起点的距离, 当前点id}heap.push({f[S], 0, S});memset(st, 0, sizeof st);//初始化次数为0//当队列不为空while(heap.size()){auto t = heap.top();heap.pop();//获取当前点的坐标以及走过的距离int u = t.e, distance = t.t;//当前点访问次数大于k,就不需要再更新了if(st[u] >= K) continue;st[u]++;//当前点次数+1if(u == T && st[u] == K) return distance;//当该点为终点时且是第k次遇到就返回当前走过的距离//遍历所有边for(int i = h[u]; ~i; i = ne[i]){int v = e[i];//当整段长度为正无穷时就跳过说明没有路径可走if(distance + w[i] + dist[v] >= INF / 2) continue;//当下个点的次数小于k时就将新的长度加入队列中if(st[v] < K){heap.push({distance + w[i] + f[u], distance + w[i], v});}}}return -1; } int main(){scanf("%d%d", &n, &m);memset(h, -1, sizeof h);memset(rh, -1, sizeof rh);while(m--){int a, b, c;scanf("%d%d%d", &a, &b, &c);add(h, a, b, c), add(rh, a, b, c);}scanf("%d%d%d", &S, &T, &K);if(S == T) K++;dijkstra();printf("%d\n", a_star());return 0; }
import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}class Pair{int f, s;public Pair(int f, int s) {this.f = f;this.s = s;}}class Point{int d, t, u;public Point(int d, int t, int u) {this.d = d;this.t = t;this.u = u;}}final int N = 1010, M = 200010, INF = 0x3f3f3f3f;int n, m;int s, t, k;int[] h = new int[N], rh = new int[N], e = new int[M], w = new int[M], ne = new int[M];int idx = 0;int[] dist = new int[N], st = new int[N], f = new int[N];void add1(int a, int b, int c){e[idx] = b;w[idx] = c;ne[idx] = h[a];h[a] = idx++;}void add2(int a, int b, int c){e[idx] = b;w[idx] = c;ne[idx] = rh[a];rh[a] = idx++;}void dijkstra(){Arrays.fill(dist, INF);PriorityQueue<Pair> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a.s));dist[t] = 0;heap.offer(new Pair(t, 0));while(!heap.isEmpty()){Pair auto = heap.poll();int u = auto.f;if(st[u] != 0) continue;st[u] = 1;for(int i = h[u]; i != -1; i = ne[i]){int v = e[i];if(dist[v] > dist[u] + w[i]){dist[v] = dist[u] + w[i];heap.offer(new Pair(v, dist[v]));}}}System.arraycopy(dist, 0, f, 0, n);}int AStar(){Arrays.fill(st, 0);PriorityQueue<Point> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a.d));heap.offer(new Point(f[s], 0, s));while(!heap.isEmpty()){Point auto = heap.poll();int u = auto.u, distance = auto.t;if(st[u] >= k) continue;st[u]++;if(st[u] == k && u == t) return distance;for(int i = h[u]; i != -1; i = ne[i]){int v = e[i];if(distance + w[i] + f[v] >= INF / 2) continue;if(st[v] < k){heap.offer(new Point(distance + w[i] + f[u], distance + w[i], v));}}}return -1;}void run(){Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();Arrays.fill(h, -1);Arrays.fill(rh, -1);for (int i = 0; i < m; i++) {int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();add1(a, b, c);add2(a, b, c);}s = sc.nextInt();t = sc.nextInt();k = sc.nextInt();sc.close();if(s == t) k++;dijkstra();System.out.println(AStar());} }
-
题目描述:给定一个3*3的网格,其中每个位置对应一个数字或为x,其中数字为从1~8中的唯一数字。我们的目的是通过交换使得网格变为正确排列。
**数据规模:**n = 3
**分析:**这里可以使用启发式算法结合广度优先来求。启发式函数就是该状态到正确排列状态的曼哈顿距离之和。
优化:
- 我们可以先对该字符串求逆序对个数,若为偶数可以直接输出不存在。
- 使用启发式函数对广度优先进行优化
题解:见分析
TIPS:无
时间复杂度:
O(n*n*logn)
#include<iostream> #include<queue> #include<algorithm> #include<unordered_map>using namespace std; typedef pair<string, char> PSC; typedef pair<int, string> PIS; #define x first #define y secondint dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1}; char ops[5] = "udlr";/*** 计算当前状态离目标状态的曼哈顿距离之和* @param state* @return*/ int f(string state){int ans = 0;for(int i = 0; i < 9; i++){if(state[i] != 'x'){int t = state[i] - '1';ans += abs(t / 3 - i / 3) + abs(t % 3 - i % 3);}}return ans; }string bfs(string start){string end = "12345678x";unordered_map<string, int> dist;//存当前状态与终点的最短距离unordered_map<string, bool> st;//标记当前状态是否已访问unordered_map<string, PSC> prev;//存之前状态转移到当前转态的方向priority_queue<PIS, vector<PIS>, greater<PIS>> heap;//前者存最少步数,后者存状态heap.push({f(start), start});dist[start] = 0;while(heap.size()){auto t = heap.top();heap.pop();string state = t.y;if(state == end) break;if(st[state]) continue;st[state] = true;int x, y;for(int i = 0; i < state.size(); i++){if(state[i] == 'x'){x = i / 3, y = i % 3;break;}}string source = state;//遍历四个方向for(int i = 0; i < 4; i++){int nx = x + dx[i], ny = y + dy[i];//看是否在范围内if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3){//与上下左右位置互换state = source;swap(state[x * 3 + y], state[nx * 3 + ny]);//若当前状态未访问过,或使用当前状态扩展能使得距离更短if(!dist.count(state) || dist[state] > dist[source] + 1){dist[state] = dist[source] + 1;prev[state] = {source, ops[i]};heap.push({dist[state] + f(state), state});}}}}string ans;while(end != start){ans += prev[end].y;end = prev[end].x;}reverse(ans.begin(), ans.end());return ans;} int main(){string start, seq;char c;for(int i = 0; i < 9; i++){cin >> c;start += c;if(c != 'x') seq += c;}int cnt = 0;//计算逆序对的个数,若逆序对为奇数则无解for (int i = 0; i < 8; i ++ )for (int j = i + 1; j < 8; j ++ )if (seq[i] > seq[j])cnt ++ ;if (cnt % 2) puts("unsolvable");else cout << bfs(start) << endl;return 0; }
import java.util.*;public class Main {public static void main(String[] args) {new Main().run();}class Pair<K, V>{K key;V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() {return key;}public V getValue() {return value;}}final int[] dx = {-1, 1, 0, 0}, dy = {0, 0, -1, 1};final char[] ops = {'u', 'd', 'l', 'r'};String bfs(String start) {String end = "12345678x";Map<String, Integer> dist = new HashMap<>();//记录当前状态的最少步数Map<String, Boolean> st = new HashMap<>();//标记数组Map<String, Pair<String, Character>> prev = new HashMap<>();//记录原状态转移的方向PriorityQueue<Pair<Integer, String>> heap = new PriorityQueue<>(Comparator.comparingInt(Pair::getKey));heap.offer(new Pair<>(f(start), start));dist.put(start, 0);while (!heap.isEmpty()) {Pair<Integer, String> t = heap.poll();String state = t.value;if(state.equals(end)) break;//若当前点为终点说明已经找到结束循环if(st.getOrDefault(state, false)) continue;//若当前状态已存在,就跳过st.put(state, true);//标记为已访问//求x的坐标int x = 0, y = 0;for(int i = 0; i < 9; i++){if(state.charAt(i) == 'x'){x = i / 3;y = i % 3;break;}}//source初始为当前状态String source = state;//向四周进行扩展for(int i = 0; i < 4; i++){int nx = x + dx[i], ny = y + dy[i];//当该位置合法时if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3){//更新当前状态为sourcestate = source;//将状态转为数组,交换这两个字符char[] stateA = state.toCharArray();char tmp = stateA[x * 3 + y];stateA[x * 3 + y] = stateA[nx * 3 + ny];stateA[nx * 3 + ny] = tmp;state = new String(stateA);//当不含当前状态或此次更新能使得state的距离更短if(!dist.containsKey(state) || dist.get(state) > dist.get(source) + 1){dist.put(state, dist.get(source) + 1);//就对路径进行更新prev.put(state, new Pair<>(source, ops[i]));//并记录原状态转移的方向heap.add(new Pair<>(dist.get(state) + f(state), state));//将f[state] + dist[state]重新加入队列中}}}}StringBuilder ans = new StringBuilder();//当end和start不等时while(!end.equals(start)){//在ans中添加上一个转移过来的方向ans.append(prev.get(end).value);//并把end向前移动end = prev.get(end).key;}return ans.reverse().toString();}/*** 启发式算法* @param state* @return*/int f(String state){int ans = 0;char[] chars = state.toCharArray();for (int i = 0; i < 9; i++) {if(chars[i] != 'x'){int t = chars[i] - '1';ans += Math.abs(i / 3 - t / 3) + Math.abs(i % 3 - t % 3);}}return ans;}void run(){String start = "", seq = new String();Scanner sc = new Scanner(System.in);for(int i = 0; i < 9; i++){char c = sc.next().charAt(0);start += c;if(c != 'x') seq += c;}int cnt = 0;//计算逆序对个数for(int i = 0; i < 8; i++){for(int j = i + 1; j < 8; j++){if(seq.charAt(i) > seq.charAt(j)) cnt++;}}if(cnt % 2 != 0) System.out.println("unsolvable");else System.out.println(bfs(start));} }
-
题目描述:给定n个数,编号为1n。在每一次操作中,可以抽取其中的连续一段,再把当前段插到其它某个位置。我们想让书按照1n排列的顺序依次排列。
**数据规模:**1 <= n <= 15
**分析:**如果我们一次拿出连续的
i
个数,则有n - i + 1
种连续数的方式。剩余还有n - i
个位置可以插入。因此这些数的选法有 15 * 14 + 14 * 13 + 13 * 12 + … + 2 * 1。在这里把一个连续的数放到某个数的后面等价于将那个数放到连续的数的前面。因此选法有(15 * 14 + 14 * 13 + 13 * 12 + … + 2 * 1) / 2 = 560。优化:
- 每个状态总分支的数量是560,考虑在四步之内解决,最多有560^4个状态,会超时。因此这里可以用IDA*来优化。
题解:
- 估价函数需要满足:不大于实际步数
- 在最终状态下,每本书后面的书的编号应该比当前书多1
- 每次移动最多会段开三个相连接的位置,因此最多会将3个错误的连接修正,所以当有tot个连接,那么最少需要[tot / 3]次操作。
- 当前估计函数可以设计为f(s) = [tot / 3]这里是上取整。
TIPS:无
时间复杂度:
O(560^4)
#include<iostream> #include<cstring> using namespace std; const int N = 15;int n; int q[N];//存编号 int w[5][N];//备份/*** 估价函数* @return*/ int f(){//统计非有序对的个数int tot = 0;//比较相邻的两个数,当后一个数不是前一个数+1时,就进行计数for(int i = 0; i < n - 1; i++)if(q[i + 1] != q[i] + 1) tot++;//最终结果要/3下取整return (tot + 2) / 3; } /*** 检查序列是否有序* @return*/ bool check(){for(int i = 0; i < n; i++)if(q[i] != i + 1) return false;return true; }bool dfs(int depth, int max_depth){//剪枝,若当前深度 + 估价函数 > 最大深度 就返回if(depth + f() > max_depth) return false;//边界条件if(check()) return true;//l、r枚举闭区间,k枚举插入的区间for(int l = 0; l < n; l++){for(int r = l; r < n; r++){for(int k = r + 1; k < n; k++){//备份当前层memcpy(w[depth], q, sizeof q);int x, y;//交换位置for(x = r + 1, y = l; x <= k; x++, y++) q[y] = w[depth][x];for(x = l; x <= r; x++, y++) q[y] = w[depth][x];if(dfs(depth + 1, max_depth)) return true;//回溯memcpy(q, w[depth], sizeof q);}}}return false; }int main(){int T;scanf("%d", &T);while(T--){scanf("%d", &n);for(int i = 0; i < n; i++) scanf("%d", &q[i]);int depth = 0;while(depth < 5 && !dfs(0, depth)) depth++;//迭代加深if(depth >= 5) puts("5 or more");else printf("%d\n", depth);}return 0; }
import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 20;int[] q = new int[N];int[][] b = new int[5][N];int n;int f(){int tot = 0;for (int i = 0; i < n - 1; i++) {if(q[i + 1] != q[i] + 1) tot++;}return (tot + 2) / 3;}boolean check(){for (int i = 0; i < n; i++) {if(q[i] != i + 1) return false;}return true;}boolean dfs(int depth, int maxDepth){if(depth + f() > maxDepth) return false;if(check()) return true;//这里l,r分别枚举左右边界,k枚举要交换的起始位置for(int l = 0; l < n; l++){for(int r = l; r < n; r++){for(int k = r + 1; k < n; k++){//交换之前备份一下,这里每层的状态可能不一样System.arraycopy(q, 0, b[depth], 0, n);int x, y;//这里x起始下标为区间的下一个位置,y为区间的左边界,将中间这快移动到前面, 区间移动到kfor(x = r + 1, y = l; x <= k; x++, y++) q[y] = b[depth][x];for(x = l; x <= r; x++, y++) q[y] = b[depth][x];//进入下一层,入口即出口if(dfs(depth + 1, maxDepth)) return true;//回溯System.arraycopy(b[depth], 0, q, 0, n);}}}return false;}void run(){Scanner sc = new Scanner(System.in);int T = sc.nextInt();while(T-- > 0){n = sc.nextInt();for (int i = 0; i < n; i++) {q[i] = sc.nextInt();}int maxDepth = 0;while(maxDepth <= 4 && !dfs(0, maxDepth)) maxDepth++;if(maxDepth <= 4) System.out.println(maxDepth);else System.out.println("5 or more");}sc.close();} }
-
题目描述:给定一个
#
的棋盘,上面有三个数字各八个。有八种操作,对应A~H
,现在我们想让中间的八个数为同一个数,求最少需要操作的步数。数据规模: n = 8
**分析:**首先想到的是深搜,对于这种图我们将对应操作的行、列存入ops中。将中间的八个数存入
center
中。 因为这里需要输出路径,我们将路径保存在path中。优化:
- 这里可以使用IDA*算法,即基于逐步加深深度搜索 + 估价函数,其中估价函数 + 当前值 <= 真实值
- 对于同一种操作,如果上一个操作和当前操作的方向正好相反就不需要重复考虑。这里使用一个数组
rev
,用来映射当前操作与之相反的操作。
题解:
- 估价函数可以用当前中间的八个数中重复次数最多的数作为max, 我们剩余要操作的步骤就是 8 - cnt 即 f = 8 - cnt
TIPS:无
时间复杂度:
O(7^k)
#include<iostream> #include<cstring> #include<algorithm> using namespace std; const int N = 24; int q[N]; int ops[8][7] = {{0, 2, 6, 11, 15, 20, 22},{1, 3, 8, 12, 17, 21, 23},{10, 9, 8, 7, 6, 5, 4},{19, 18, 17, 16, 15, 14, 13},{23, 21, 17, 12, 8, 3, 1},{22, 20, 15, 11, 6, 2, 0},{13, 14, 15, 16, 17, 18, 19},{4, 5, 6, 7, 8, 9, 10} }; int rev[8] = {5, 4, 7, 6, 1, 0, 3, 2}; int center[8] = {6, 7, 8, 11, 12, 15, 16, 17}; int path[100]; int cnt[3]; /*** 估价函数* @return*/ int f(){memset(cnt, 0, sizeof cnt);for(int i = 0; i < 8; i++) cnt[q[center[i]] - 1]++;int m = 0;for(int i = 0; i < 3; i++) m = max(m, cnt[i]);return 8 - m; }bool check(){for(int i = 1; i < 8; i++) if(q[center[0]] != q[center[i]]) return false;return true; }void operate(int x){int t = q[ops[x][0]];for(int i = 1; i < 7; i++) q[ops[x][i - 1]] = q[ops[x][i]];q[ops[x][6]] = t; } bool dfs(int depth, int max_depth, int last){if(depth + f() > max_depth) return false;if(!f()) return true;for(int i = 0; i < 8; i++){if(last == rev[i]) continue;operate(i);path[depth] = i;if(dfs(depth + 1, max_depth, i)) return true;operate(rev[i]);}return false; } int main(){while(scanf("%d", &q[0]), q[0]){for(int i = 1; i < N; i++) scanf("%d", &q[i]);int depth = 0;while(!dfs(0, depth, -1)) depth++;if(!depth) {puts("No moves needed");printf("%d\n", q[6]);}else{for(int i = 0; i < depth; i++) printf("%c", path[i] + 'A');printf("\n%d\n", q[6]);}}return 0; }
import java.util.Arrays; import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 24;int[] q = new int[N];int[] path = new int[100];int[] cnt = new int[3];int[][] ops = {{0, 2, 6, 11, 15, 20, 22},{1, 3, 8, 12, 17, 21, 23},{10, 9, 8, 7, 6, 5, 4},{19, 18, 17, 16, 15, 14, 13},{23, 21, 17, 12, 8, 3, 1},{22, 20, 15, 11, 6, 2, 0},{13, 14, 15, 16, 17, 18, 19},{4, 5, 6, 7, 8, 9, 10}};int[] center = {6, 7, 8, 11, 12, 15, 16, 17};int[] rev = {5, 4, 7, 6, 1, 0, 3, 2};int f(){Arrays.fill(cnt, 0);for(int i = 0; i < 8; i++) cnt[q[center[i]] - 1]++;int max = 0;for (int i = 0; i < 3; i++) max = Math.max(max, cnt[i]);return 8 - max;}boolean check(){for(int i = 1; i < 8; i++) if(q[center[0]] != q[center[i]]) return false;return true;}void operate(int x){int t = q[ops[x][0]];for(int i = 0; i < 6; i++) q[ops[x][i]] = q[ops[x][i + 1]];q[ops[x][6]] = t;}boolean dfs(int depth, int maxDepth, int last){if(depth + f() > maxDepth) return false;if(f() == 0) return true;for(int i = 0; i < 8; i++){if(last == rev[i]) continue;operate(i);path[depth] = i;if(dfs(depth + 1, maxDepth, i)) return true;operate(rev[i]);}return false;}void run(){Scanner sc = new Scanner(System.in);while(true){q[0] = sc.nextInt();if(q[0] == 0) break;for (int i = 1; i < N; i++) q[i] = sc.nextInt();int depth = 0;while(!dfs(0, depth, -1)) depth++;if(depth == 0){System.out.println("No moves needed");System.out.println(q[6]);}else{for (int i = 0; i < depth; i++) System.out.print((char) (path[i] + 'A'));System.out.printf("\n%d\n", q[6]);}}sc.close();} }
-
题目描述:给定一个正方形它由若干根火柴构成,正方形可能缺失了一些火柴,现在我们想去除其中的一些火柴使得火柴不能构成正方形。那么至少要去除多少根火柴?
数据规模: n <= 5
**分析:**问题等价于
从现有火柴中,至少选择多少根火柴,能使得正方形中都被选了一根火柴。
这是重复覆盖问题可以用dfs来做。优化:
- 每次选择一个还没有被覆盖的正方形(选择最小的一个),枚举选择它上面的哪根火柴。
- 估价函数设计:枚举每一个正方形,若当前正方形还是完整的,那么直接删掉它的所有边,只记删除一次。小于等于真实值。
题解:见分析
TIPS:无
时间复杂度:
O(60^k)
#include<iostream> #include<cstring> #include<vector>using namespace std;const int N = 65; //网格最大是5 * 5的最多会有 5 * (5 + 1) * 2 = 60个正方形 int n, idx; //n为网络规模,idx为正方形数量 vector<int> square[N]; //存每个正方形的火柴编号 bool st[N]; //标记当前火柴是否已经被移除//新加一个正方形,左上角坐标为(r,c),边长为len的正方形 void add(int r, int c, int len){int d = n << 1 | 1;//即2n + 1vector<int> &s = square[idx];//将上一组测试内容清空s.clear();for(int i = 0; i < len; i++){//第i个s.push_back(d * r + c + i + 1); //上s.push_back(d * (r + len) + c + i + 1);//下s.push_back((r + i) * d + n + c + 1);//左s.push_back((r + i) * d + n + c + len + 1 );//左} // for(int x : s) printf("%d ", x); // puts("");idx++;} /*** 判断正方形是否完整* @param s* @return*/ bool check(vector<int> &s){for(int x : s){if(st[x]) return false;}return true; } /*** 估价函数,若当前正方形还是完整就删除掉它的所有边,并只记一次* @return*/ int f(){static bool b[N];//备份数组,用于存stmemcpy(b, st, sizeof st);int ans = 0;for(int i = 0; i < idx; i++){//枚举所有正方形if(check(square[i])){ans++;for(int x : square[i]){st[x] = true;}}}memcpy(st, b, sizeof b);//复制回来return ans; } bool dfs(int depth, int max_depth){if(depth + f() > max_depth) return false;for(int i = 0; i < idx; i++){//枚举所有正方形if(check(square[i])){//若第i个正方形未被破坏//就枚举该正方形的所有边,并去掉该边,继续搜索for(int x : square[i]){st[x] = true;if(dfs(depth + 1, max_depth)) return true;st[x] = false;//回溯}//这里若每条边都不可行,就返回falsereturn false;}}return true;//这里所有正方形都被破坏了,返回true; } int main(){int T;scanf("%d", &T);while(T--){scanf("%d", &n), idx = 0;//初始化idxmemset(st, false, sizeof st);for(int len = 1; len <= n; len++){//枚举len、r、c,预处理每个正方形for(int r = 0; r + len <= n; r++){for(int c = 0; c + len <= n; c++)add(r, c, len);}}int k;scanf("%d", &k);while(k--){//将这些边标记为已移除int x;scanf("%d", &x);st[x] = true;}int depth = 0;while(!dfs(0, depth)) depth++;printf("%d\n", depth);}return 0; }
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 65;int n, idx;List<Integer>[] square = new List[N];boolean[] st = new boolean[N];int maxDepth;void add(int r, int c, int len) {int d = n << 1 | 1;square[idx] = new ArrayList<>();List<Integer> s = square[idx];for (int i = 0; i < len; i++) {s.add(d * r + c + i + 1);s.add(d * (r + len) + c + i + 1);s.add(d * (r + i) + c + n + 1);s.add(d * (r + i) + c + n + len + 1);}idx++;}boolean check(int s) {for (int i : square[s]) if (st[i]) return false;return true;}int f() {boolean[] b = Arrays.copyOf(st, N);int cnt = 0;for (int i = 0; i < idx; i++) {if (check(i)){cnt++;for (int x : square[i]) {st[x] = true;}}}st = Arrays.copyOf(b, N);return cnt;}boolean dfs(int depth) {if (depth + f() > maxDepth) return false;for (int i = 0; i < idx; i++) {if (check(i)) {for (int x : square[i]) {st[x] = true;if (dfs(depth + 1)) return true;st[x] = false;}return false;}}return true;}void run() {Scanner sc = new Scanner(System.in);int T = sc.nextInt();while (T-- > 0) {n = sc.nextInt();idx = 0;Arrays.fill(st, false);for (int len = 1; len <= n; len++) {for (int r = 0; r + len <= n; r++) {for (int c = 0; c + len <= n; c++) {add(r, c, len);}}}int k = sc.nextInt();while (k-- > 0) {int x = sc.nextInt();st[x] = true;}maxDepth = 0;while (!dfs(0)) maxDepth++;System.out.println(maxDepth);}sc.close();} }
-
题目描述:给定一个(9*9)的数独矩阵,这个矩阵每个位置都有一个得分。从中心向四周扩展,每向前一步就减1。现在要将数独填满,求最高的得分和是多少?
数据规模: n = 9
**分析:**数独问题我们可以用深搜来做,对于要求最高的得分,我们可以在搜索时增加一个属性当前层的得分,我们要在所有满足边界条件时求最大的得分。
优化:
- 搜索顺序:我们优先将可选项最少的位置开始填。
题解:
- 我们先将row、col、cell预处理,代表所有数都可选的状态。然后预处理出0 ~ 2^9 - 1这些二进制数,将它们二进制下对应1的个数存入ones中。将每个位置的分数预处理存入score中。
- 根据输入的情况填标记数组g,和row、col、cell将当前已选的数标记为0,并求当前的分数累计之和,求未填项的个数。
- 进行搜索,每次扫描标记数组g看哪个位置可选项最少,就从该位置开始搜索。
- 满足边界条件时,更新当前ans
TIPS:无
时间复杂度:
O(n^81)
#include<iostream> #include<algorithm> using namespace std;const int N = 9, INF = 10; int row[N], col[N], cell[N]; int ones[1 << N], log_arr[1 << N]; int score[N * N]; char g[N][N]; int ans = -1; /*** 获取二进制最低位1* @param x* @return*/ inline int lowbit(int x){return x & -x; } /*** 初始化二进制数组和得分*/ void init(){for(int i = 0; i < N; i++) {row[i] = (1 << N) - 1;col[i] = (1 << N) - 1;cell[i] = (1 << N) - 1;}for(int i = 0; i < 1 << N; i++){for(int j = i; j; j -= lowbit(j)){ones[i]++;}}for(int i = 0; i < N; i++) {log_arr[1 << i] = i;}for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){int idx = i * N + j;score[idx] = min(min(i, N - 1 - i), min(j, N - 1 - j)) + 6;}} } /*** 获取当前可选项* @param x* @param y* @return*/ int get(int x, int y){return row[x] & col[y] & cell[x / 3 * 3 + y / 3]; } /*** 更改二进制数组和字符阵列* @param x* @param y* @param t*/ void draw(int x, int y, int t){if(t > 0){//填数row[x] -= 1 << (t - 1);col[y] -= 1 << (t - 1);cell[x / 3 * 3 + y / 3] -= 1 << (t - 1);g[x][y] = (char)('0' + t);}else if(t == 0){g[x][y] = '0';}else {//不填数,清空t = -t;row[x] += 1 << (t - 1);col[y] += 1 << (t - 1);cell[x / 3 * 3 + y / 3] += 1 << (t - 1);g[x][y] = '0';} }/*** 获取得分* @param x* @param y* @param t* @return*/ int get_score(int x, int y, int t){return score[x * N + y] * t; } /*** 深度优先搜索* @param cnt* @param score*/ void dfs(int cnt, int sum){if(!cnt) {ans = max(ans, sum);return;}//剪枝int x, y, min = INF;//扫描字符阵列for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){//若当前位置没填的话,就看看当前位置有多少可选项if(g[i][j] == '0'){int t = ones[get(i, j)];//若当前可选项个数小于min,就将min更新为t,将x、y更新为i,jif(t < min) {min = t;x = i, y = j;}}}}//获取每个位置的1,并看这个1是第几位即是那个数for(int i = get(x, y); i; i -= lowbit(i)){int t = log_arr[lowbit(i)] + 1;//将当前数填上draw(x, y, t);//继续向下搜索dfs(cnt - 1, sum + get_score(x, y, t));//撤销当前操作draw(x, y, -t);} }int main(){init();int cnt = 0, sum = 0;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){char t[2];cin >> t;int num = t[0] - '0';draw(i, j, num);if(num)sum += get_score(i, j, num);else cnt++;}}dfs(cnt, sum);cout << ans << endl;return 0; }
import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 9, INF = 10;int[] row = new int[N], col = new int[N], cell = new int[N];int[] ones = new int[1 << N], log = new int[1 << N];int[] score = new int[N * N];int[][] g = new int[N][N];int ans = -1;void init(){for(int i = 0; i < N; i++){row[i] = (1 << N) - 1;col[i] = (1 << N) - 1;cell[i] = (1 << N) - 1;}for(int i = 0; i < 1 << N; i++){for(int j = i; j > 0; j -= lowbit(j)){ones[i]++;}}for(int i = 0; i < N; i++) log[1 << i] = i;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){int idx = i * N + j;score[idx] = Math.min(Math.min(i, N - 1 - i), Math.min(j, N - j - 1)) + 6;}}}void draw(int x, int y, int t){if(t > 0){row[x] -= 1 << (t - 1);col[y] -= 1 << (t - 1);cell[x / 3 * 3 + y / 3] -= 1 << (t - 1);g[x][y] = t;}else if(t == 0){g[x][y] = 0;}else{t = -t;row[x] += 1 << (t - 1);col[y] += 1 << (t - 1);cell[x / 3 * 3 + y / 3] += 1 << (t - 1);g[x][y] = 0;}}int getScore(int x, int y, int t){return score[x * N + y] * t;}int get(int x, int y){return row[x] & col[y] & cell[x / 3 * 3 + y / 3];}void dfs(int cnt, int sum){if(cnt == 0){ans = Math.max(ans, sum);return;}int x = 0, y = 0, min = INF;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(g[i][j] == 0){int t = ones[get(i, j)];if(t < min){min = t;x = i;y = j;}}}}for(int i = get(x, y); i > 0; i -= lowbit(i)){int t = log[lowbit(i)] + 1;draw(x, y, t);dfs(cnt - 1, sum + getScore(x, y, t));draw(x, y, -t);}}int lowbit(int x){return x & -x;}void run(){Scanner sc = new Scanner(System.in);init();int cnt = 0, sum = 0;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){int t = sc.nextInt();draw(i, j, t);if(t > 0) sum += getScore(i, j, t);else cnt++;}}dfs(cnt, sum);System.out.println(ans);sc.close();} }
-
题目描述:给定一个算式及进制求算式中各个字母分别代表的数字是多少?
数据规模: N <= 26
**分析:**这里显然要使用深搜来做,重点是如何优化。
优化:
- 从右到左的顺序来枚举每个字母所代表的数字。
- 若后面所有字母都是确定的,则可以直接判断每一位是否合法;若后面某些字母没有确定,则有两种情况。1.进位是0,(A + B + 0) % N == c 2.进位是1,(A + B + 1) % N == c
- 最高位不能有进位。
题解:
- 先将输入按从右到左从上到下的顺序,将字母按第一次出现的编号加入q中,方便在dfs时按这个顺序赋值与计算。
- 搜索-dfs(),这里我们先看当前数是否已经使用过,若没有就标记并尝试将path中q对应的该位置赋为该值,每次向下搜索时先进行剪枝操作。
- 剪枝-check(),同样是按从右到左的顺序,分别获取b、c、d看当前位置是否已经赋值,若赋过值就先看余数是否存在,若存在就看当前式子是否合法并更新r。若不存在就按余数为0或余数为1对当前式子进行合法性判断。若当前式子未赋值就标记余数为-1.
- 输出path
TIPS:无
时间复杂度:
O(26!*26)
#include<iostream> #include<cstring>using namespace std; const int N = 30; int n; char e[3][N];//存数组 int q[N], path[N];//存序列号、存答案 bool st[N];//表示当前字符是否访问过 bool check(){for(int i = n - 1, t = 0; i >= 0; i--){int a = e[0][i] - 'A', b = e[1][i] - 'A', c = e[2][i] - 'A';if(path[a] != -1 && path[b] != -1 && path[c] != -1){//若这三位数都确定下来了a = path[a], b = path[b], c = path[c];if(t != -1){//若前式确定下来了,存在余数if((a + b + t) % n != c) return false;//若当前组合不合法返回falseif(!i && a + b + t >= n) return false;//若最高位还有余数返回falset = (a + b + t) / n;}else{//若不存在与余数if((a + b + 0) % n != c && (a + b + 1) % n != c) return false;if(!i && a + b >= n) return false;}}else t = -1;}return true; }bool dfs(int u){//边界条件if(u == n) return true;for(int i = 0; i < n; i++){if(!st[i]){//若该数没填就填它st[i] = true;path[q[u]] = i;if(check() && dfs(u + 1)) return true;st[i] = false;path[q[u]] = -1;}}return false; } int main(){scanf("%d", &n);for(int i = 0; i < 3; i++) {scanf("%s", e[i]);}//从右到左从上到下,k用来维护当前字符的编号for(int i = n - 1, k = 0; i >= 0; i--){for(int j = 0; j < 3; j++){int t = e[j][i] - 'A';if(!st[t]){st[t] = true;q[k++] = t;}}}memset(st, false, sizeof st);memset(path, -1, sizeof path);dfs(0);for(int i = 0; i < n; i++) printf("%d ", path[i]);return 0; }
import java.util.Arrays; import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 30;char[][] a = new char[3][];int[] q = new int[N], path = new int[N];boolean[] st = new boolean[N];int n;boolean check(){for(int i = n - 1, r = 0; i >= 0; i--){int b = a[0][i] - 'A', c = a[1][i] - 'A', d = a[2][i] - 'A';if(path[b] != -1 && path[c] != -1 && path[d] != -1){b = path[b];c = path[c];d = path[d];if(r != -1){if((b + c + r) % n != d) return false;if(i == 0 && b + c + r >= n) return false;r = (b + c + r) / n;}else{if((b + c + 0) % n != d && (b + c + 1) % n != d) return false;if(i == 0 && (b + c) >= n) return false;}}else r = -1;}return true;}boolean dfs(int u){if(u == n) return true;for(int i = 0; i < n; i++) {if(!st[i]){st[i] = true;path[q[u]] = i;if(check() && dfs(u + 1)) return true;st[i] = false;path[q[u]] = -1;}}return false;}void run(){Scanner sc = new Scanner(System.in);n = sc.nextInt();for (int i = 0; i < 3; i++) {a[i] = sc.next().toCharArray();}for(int i = n - 1, k = 0; i >= 0; i--){for(int j = 0; j < 3; j++){int t = a[j][i] - 'A';if(!st[t]){st[t] = true;q[k++] = t;}}}Arrays.fill(path, -1);Arrays.fill(st, false);dfs(0);for(int i = 0; i < n; i++) System.out.printf("%d ", path[i]);sc.close();} }
-
题目描述:给定一个7*5列的棋盘,里面有很多种颜色的方块。相邻的两个方块可以左右交换位置,方块不允许悬空。当同一行或同一列有三个或三个以上方块是相邻的,这三个方块会同时消失。注意一个方块可以同时属于一行或一列,也就是说当行和列同时满足时,这些方块会同时消失。若能正好在n次交换后使得棋盘中的全部方块消失,就输出对应方块执行的交换操作,这里有多组测试数据时要求输出字典序最小的一组解。若没有解决方案,则输出-1
数据规模: 0 <= n <= 5
**分析:**该题是带有搜索的模拟题,可以用深搜来做。
优化:
- 每一步,一次枚举操作哪个格子,再枚举向哪个方向移动。
- 若某种颜色只有1个或两个小方块,则直接回溯。
- 枚举向左移动时,如果左边有方块,则直接回溯。我们只考虑向右边移动。
题解: 见分析
TIPS:无
时间复杂度:
O((nm)^5)
#include<iostream> #include<cstring> #include<algorithm>using namespace std; const int N = 5, M = 7; int n; int g[N][M], bg[N][N][M]; int cnt[11], bcnt[N][11]; bool st[N][M];struct Point{int x, y, d; }path[5];void move(int a, int b, int c){swap(g[a][b], g[c][b]);while(true){bool flag = true;//处理悬空格for(int x = 0; x < N; x++){int z = 0;for(int y = 0; y < M; y++){if(g[x][y])g[x][z++] = g[x][y];}while(z < M) g[x][z++] = 0;}memset(st, false, sizeof st);//枚举左右上下,对可以删除的格子进行标记for(int x = 0; x < N; x++){for(int y = 0; y < M; y++){if(g[x][y]){//横向int l = x, r = x;while(l - 1 >= 0 && g[l - 1][y] == g[x][y]) l--;while(r + 1 < N && g[r + 1][y] == g[x][y]) r++;if(r - l + 1 >= 3){st[x][y] = true;flag = false;}//纵向l = r = y;while(l - 1 >= 0 && g[x][l - 1] == g[x][y]) l--;while(r + 1 < M && g[x][r + 1] == g[x][y]) r++;if(r - l + 1 >= 3){st[x][y] = true;flag = false;}}}}if(flag) break;//对标记的单元格进行删除for(int x = 0; x < N; x++){for(int y = 0; y < M; y++){if(st[x][y]){cnt[0]--;cnt[g[x][y]]--;g[x][y] = 0;}}}} }bool dfs(int u){if(u == n) return !cnt[0];//边界条件当执行第n个操作,且当前图中没有方块//第一次剪枝for(int i = 1; i <= 10; i++)if(cnt[i] == 1 || cnt[i] == 2) return false;//枚举所有操作memcpy(bg[u], g, sizeof g);memcpy(bcnt[u], cnt, sizeof cnt);for(int x = 0; x < N; x++){for(int y = 0; y < M; y++){if(g[x][y]){int nx = x + 1;if(nx < N){path[u] = {x, y, 1};move(x, y, nx);if(dfs(u + 1)) return true;//回溯memcpy(g, bg[u], sizeof g);memcpy(cnt, bcnt[u], sizeof cnt);}//第二次剪枝nx = x - 1;if(nx >= 0 && !g[nx][y]){path[u] = {x, y, -1};move(x, y, nx);if(dfs(u + 1)) return true;memcpy(g, bg[u], sizeof g);memcpy(cnt, bcnt[u], sizeof cnt);}}}}return false; }int main(){scanf("%d", &n);//枚举每一列for(int x = 0; x < N; x++){int t, y = 0;//当该行值不为0时,就分别对总方块个数、当前颜色个数进行计数,图进行赋值while(scanf("%d", &t), t){cnt[0]++;cnt[t]++;g[x][y++] = t;}}if(dfs(0)){for(int i = 0; i < n; i++) printf("%d %d %d\n", path[i].x, path[i].y, path[i].d);}else puts("-1");return 0; }
import java.util.Arrays; import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}class Block{int x, y, d;public Block(int x, int y, int d) {this.x = x;this.y = y;this.d = d;}}final int N = 5, M = 7;int[][] g = new int[N][M];int[] cnt = new int[11];boolean[][] st = new boolean[N][M];int[][][] bg = new int[N][N][M];int[][] bcnt = new int[N][11];Block[] path = new Block[N];int n;void move(int a, int b, int c){int t = g[a][b];g[a][b] = g[c][b];g[c][b] = t;while(true){boolean flag = true;//将空位去除for (int i = 0; i < N; i++) {int k = 0;for (int j = 0; j < M; j++) {if(g[i][j] > 0){g[i][k++] = g[i][j];}}while(k < M) g[i][k++] = 0;}//先清除所有标记for (int i = 0; i < N; i++) {Arrays.fill(st[i], false);}//让连续的砖块消失,这里用到了延迟删除for (int i = 0; i < N; i++) {for (int j = 0; j < M; j++) {if(g[i][j] > 0){int l = i, r = i;while(l - 1 >= 0 && g[l - 1][j] == g[i][j]) l--;while(r + 1 < N && g[r + 1][j] == g[i][j]) r++;if(r - l + 1 >= 3) {st[i][j] = true;flag = false;}l = r = j;while(l - 1 >= 0 && g[i][l - 1] == g[i][j]) l--;while(r + 1 < M && g[i][r + 1] == g[i][j]) r++;if(r - l + 1 >= 3) {st[i][j] = true;flag = false;}}}}//若没有要更新的if(flag) break;for(int i = 0; i < N; i++)for(int j = 0; j < M; j++)if(st[i][j]){cnt[0]--;cnt[g[i][j]]--;g[i][j] = 0;}}}boolean dfs(int u){if(u == n) return cnt[0] == 0;//第一次剪枝for (int i = 1; i <= 10; i++)if(cnt[i] == 1 || cnt[i] == 2) return false;for(int i = 0; i < N; i++){System.arraycopy(g[i], 0, bg[u][i], 0, M);}System.arraycopy(cnt, 0, bcnt[u], 0, 11);for (int i = 0; i < N; i++) {for (int j = 0; j < M; j++) {if(g[i][j] > 0){int nx = i + 1;if(nx < N){path[u] = new Block(i, j, 1);move(i, j, nx);if(dfs(u + 1)) return true;for(int k = 0; k < N; k++){System.arraycopy(bg[u][k], 0, g[k], 0, M);}System.arraycopy(bcnt[u], 0, cnt, 0, 11);}nx = i - 1;if(nx >= 0 && g[nx][j] == 0){path[u] = new Block(i, j, -1);move(i, j, nx);if(dfs(u + 1)) return true;for(int k = 0; k < N; k++){System.arraycopy(bg[u][k], 0, g[k], 0, M);}System.arraycopy(bcnt[u], 0, cnt, 0, 11);}}}}return false;}void run(){Scanner sc = new Scanner(System.in);n = sc.nextInt();for (int i = 0; i < N; i++) {int j = 0, t;while(true){t = sc.nextInt();if(t == 0) break;cnt[0]++;cnt[t]++;g[i][j++] = t;}}sc.close();if(dfs(0)){for(int i = 0; i < n; i++) System.out.printf("%d %d %d\n", path[i].x, path[i].y, path[i].d);}else System.out.println("-1");} }
-
题目描述:一个男子在
12:00
到达某车站,并在12:00 ~ 12:59
期间在那里逗留。已给出巴士抵达的时间,求下列所有巴士在满足到达本站输入数据的情况下,求总路线最少是多少?数据规模: n = 60
**分析:**我们可以先预处理出所有可能的线路。先枚举起点i,再枚举公差j。i 和 j需要满足两个条件:
-
因为 i 是起点,因此
0 ~ i - 1
中不能包含任何该序列的点,公差至少从i + 1开始 -
因为
0 ~ 59
之间至少要包含两个点, 因此i + j < 60
问题就转化为,至少从合法线路中选出多少条,才可以覆盖所有给定的公交车。这里答案比较浅。因此可以采用迭代加深来搜索。
优化:
- 因为是枚举组合数,并不是排列数,为了避免重复在枚举时传入当前起点start
- 将所有等差序列按长度排序,优先枚举长度较长的等差数列。这样在搜索树中前几层分支较少,可以快速的发现矛盾并回溯。
- 由于优化2的存在,当前路线覆盖的点数是最多的。若当前路线覆盖的点数 * 剩余可选路径条数 + 当前已选覆盖点数 < 总点数,则说明该方案一定非法,直接回溯。
题解: 见分析
TIPS:无
时间复杂度:
O(
C C 60 2 17 C_{C_{60}^{2}}^{17} CC60217
)
#include<iostream> #include<algorithm> #include<vector>using namespace std;const int N = 2000, M = 60;struct Point{int a, b, c;bool operator < (const Point &b) const{return a > b.a;} }; vector<Point> routes; int bus[M];//表示第t时刻到站的巴士数量 int n, max_depth; /*** 检查当前起点和公差是否合法* @param a* @param d* @return*/ bool check(int a, int d){for(int i = a; i < M; i += d){if(!bus[i]) return false;}return true; }bool dfs(int depth, int sum, int start){if(depth == max_depth) return sum == n;if(routes[start].a * (max_depth - depth) + sum < n) return false;for(int i = start; i < routes.size(); i++){auto r = routes[i];int a = r.b, d = r.c;if(!check(a, d)) continue;for(int j = a; j < 60; j += d) bus[j]--;if(dfs(depth + 1, sum + r.a, i)) return true;for(int j = a; j < 60; j += d) bus[j]++;}return false; }int main(){scanf("%d", &n);for(int i = 0; i < n; i++){int t;scanf("%d", &t);bus[t]++;}//预处理for(int i = 0; i < M; i++){for(int j = i + 1; i + j < M; j++){if(check(i, j)) routes.push_back({(M - 1 - i) / j + 1, i, j});//当前路径车的数量}}sort(routes.begin(), routes.end());max_depth = 0;while(!dfs(0, 0, 0)) max_depth++;printf("%d\n", max_depth);return 0; }
import java.util.*;public class Main {public static void main(String[] args) {new Main().run();}int N = 2000, M = 60;class Point{int a, b, c;public Point(int a, int b, int c) {this.a = a;this.b = b;this.c = c;}@Overridepublic String toString() {return "Point{" +"a=" + a +", b=" + b +", c=" + c +'}';}}List<Point> routes = new ArrayList<>();int[] bus = new int[M];int n, maxDepth;boolean check(int a, int d){for(int i = a; i < M; i += d){if(bus[i] == 0) return false;}return true;}boolean dfs(int u, int sum, int start){if(u == maxDepth) return sum == n;if(routes.get(start).a * (maxDepth - u) + sum < n) return false;for (int i = start; i < routes.size(); i++) {Point t = routes.get(i);int a = t.b, d = t.c;if(!check(a, d)) continue;for(int j = a; j < M; j += d) bus[j]--;if(dfs(u + 1, sum + t.a, i)) return true;for(int j = a; j < M; j += d) bus[j]++;}return false;}void run(){Scanner sc = new Scanner(System.in);n = sc.nextInt();for (int i = 0; i < n; i++) {int t = sc.nextInt();bus[t]++;}for(int i = 0; i < M; i++){for (int j = i + 1; i + j < M; j++) {if(check(i, j)) routes.add(new Point((M - 1 - i) / j + 1, i, j));}}Collections.sort(routes, (o1, o2) -> o2.a - o1.a);maxDepth = 0;while(!dfs(0, 0, 0)) maxDepth++;System.out.println(maxDepth);} }
-
-
题目描述:给定一串序列,我们想将序列中的元素从左到右依次加入不同的组中,使得组中的元素严格单调。现在想知道最少要分多少组?
数据规模: 1 <= n <= 50
**分析:**很显然这是一道搜索题,而且问的是最少是多少组,因此我们可以用迭代加深来做。
- 先枚举将该数加到单调递增的序列中,还是单调下降的序列中
- 若该数放到单调递增的序列中,则枚举将该数放到哪个单调递增的序列后
- 若该数放到单调递减的序列中,则枚举将该数放到哪个单调递降的序列后
优化:
- up[]存储当前所有上升子序列的末尾元素
- down[]存储当前所有下降子序列的末尾元素
- 基于贪心的思想,我们每次枚举最合适的节点插入,若该节点也不满足其它节点肯定也不会满足。
题解: 见分析
TIPS:无
时间复杂度
O(n*2^n)
#include<iostream>using namespace std; const int N = 60; int n; int h[N], up[N], down[N]; int depth; /**** @param u 当前层数* @param su 升序序列的个数* @param sd 降序序列的个数* @return*/ bool dfs(int u, int su, int sd){if(su + sd > depth) return false;if(u == n) return true;//枚举上升序列中的情况bool flag = false;for(int i = 1; i <= su; i++){if(up[i] < h[u]){int t = up[i];up[i] = h[u];if(dfs(u + 1, su, sd)) return true;up[i] = t;flag = true;break;}}if(!flag){up[su + 1] = h[u];if(dfs(u + 1, su + 1, sd)) return true;}//枚举下降子序列中的情况flag = false;for(int i = 1; i <= sd; i++){if(down[i] > h[u]){int t = down[i];down[i] = h[u];if(dfs(u + 1, su, sd)) return true;down[i] = t;flag = true;break;}}if(!flag){down[sd + 1] = h[u];if(dfs(u + 1, su, sd + 1)) return true;}return false; } int main(){while(cin >> n, n){for(int i = 0; i < n; i++) cin >> h[i];depth = 0;while(!dfs(0, 0, 0)) depth++;cout << depth << endl;}return 0; }
import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 60;int[] h = new int[N], up = new int[N], down = new int[N];int depth, n;boolean dfs(int u, int su, int sd){if(su + sd > depth) return false;if(u == n) return true;//枚举上升子序列boolean flag = false;for (int i = 1; i <= su; i++) {if(up[i] < h[u]){int t = up[i];up[i] = h[u];flag = true;if(dfs(u + 1, su, sd)) return true;up[i] = t;break;}}if(!flag){up[su + 1] = h[u];if(dfs(u + 1, su + 1, sd)) return true;}//枚举下降子序列flag = false;for(int i = 1; i <= sd; i++){if(down[i] > h[u]){int t = down[i];down[i] = h[u];flag = true;if(dfs(u + 1, su, sd)) return true;down[i] = t;break;}}if(!flag){down[sd + 1] = h[u];if(dfs(u + 1, su, sd + 1)) return true;}return false;}void run(){Scanner sc = new Scanner(System.in);while(true){n = sc.nextInt();if(n == 0) break;for (int i = 0; i < n; i++) h[i] = sc.nextInt();depth = 0;while(!dfs(0, 0, 0)) depth++;System.out.println(depth);}sc.close();} }
-
题目描述:给定一个地图,以及起点和终点。地图中有墙和可以移动的区域。人从起始位置开始,可以以日字形跳跃。每次跳跃步数+1。现在想知道从起点到终点至少需要多少步。
数据规模: 1 <= n <= 150
**分析:**很显然这里可以用宽搜来做,基于dijkstra算法的思想。
**优化:**无
题解: 见分析
TIPS:这里跳跃时可以向八个方向跳跃。
时间复杂度
O(n*n)
#include<iostream> #include<cstring> #include<queue>#define x first #define y second using namespace std; typedef pair<int, int> PII; const int N = 160; int dist[N][N]; char g[N][N]; int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1}; int n, m;int bfs(PII start, PII end){memset(dist, -1, sizeof dist);queue<PII> q;q.push({start.x, start.y});dist[start.x][start.y] = 0;while(q.size()){auto t = q.front();q.pop();if(make_pair(t.x, t.y) == end) break;for(int i = 0; i < 8; i++){int nx = t.x + dx[i], ny = t.y + dy[i];if(nx >= 0 && nx < n && ny >= 0 && ny < m){if(g[nx][ny] == '*') continue;if(dist[nx][ny] > 0) continue;dist[nx][ny] = dist[t.x][t.y] + 1;q.push({nx, ny});}}}return dist[end.x][end.y]; } int main(){cin >> m >> n;for(int i = 0; i < n; i++) cin >> g[i];PII start, end;for(int i = 0; i < n; i++)for(int j = 0; j < m; j++)if(g[i][j] == 'K') start = {i, j};else if(g[i][j] == 'H') end = {i, j};cout << bfs(start, end) << endl;return 0; }
import java.util.*;public class Main {public static void main(String[] args) {new Main().run();}class Pair{int x, y;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Pair pair = (Pair) o;return x == pair.x &&y == pair.y;}@Overridepublic int hashCode() {return Objects.hash(x, y);}public Pair(int x, int y) {this.x = x;this.y = y;}}final int N = 160;char[][] g = new char[N][];int[][] dist = new int[N][N];int[] dx = {-2, -1, 1, 2, 2, 1, -1, -2}, dy = {1, 2, 2, 1, -1, -2, -2, -1};int n, m;int bfs(Pair start, Pair end){for (int i = 0; i < n; i++) Arrays.fill(dist[i], -1);Queue<Pair> queue = new ArrayDeque<>();dist[start.x][start.y] = 0;queue.offer(new Pair(start.x, start.y));while(!queue.isEmpty()){Pair t = queue.poll();int x = t.x, y = t.y;if(t.equals(end)) break;for (int i = 0; i < 8; i++) {int nx = x + dx[i], ny = y + dy[i];if(nx >= 0 && nx < n && ny >= 0 && ny < m){if(g[nx][ny] == '*') continue;if(dist[nx][ny] != -1) continue;dist[nx][ny] = dist[x][y] + 1;queue.offer(new Pair(nx, ny));}}}return dist[end.x][end.y];}void run(){Scanner sc = new Scanner(System.in);m = sc.nextInt();n = sc.nextInt();for(int i = 0; i < n; i++) g[i] = sc.next().toCharArray();Pair start = new Pair(0, 0), end = new Pair(0, 0);for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if(g[i][j] == 'K') start = new Pair(i, j);else if(g[i][j] == 'H') end = new Pair(i, j);}}sc.close();System.out.println(bfs(start, end));} }
-
题目描述:给定两个字符串以及若干规则,问能否通过这些规则在十步之内将字符1转换为字符2?若可以输出步数,否则输出-1
数据规模: 1 <= n <= 10, 1 <= l <= 20
**分析:**我们可以将字符串抽象成图,若对应的字符字串能转换为另外一个字串,则这两个字串之间存在权值为1的边。由于这里是求步数最少因此我们可以使用广度优先算法,由于状态空间很大,这里采用双向bfs搜索。
**优化:**无
题解: 见分析
TIPS:这里需要逐层搜索,然后判断。
时间复杂度
O((l*n)^5)
#include<iostream> #include<queue> #include<unordered_map>using namespace std;const int N = 6; int n; string A, B; string a[N], b[N];int extend(queue<string>& q, unordered_map<string, int>& da, unordered_map<string, int>& db, string a[N], string b[N]){int d = da[q.front()];while(q.size() && da[q.front()] == d){auto t = q.front();q.pop();for(int i = 0; i < n; i++)for(int j = 0; j < t.size(); j++)if(t.substr(j, a[i].size()) == a[i]){string r = t.substr(0, j) + b[i] + t.substr(j + a[i].size());if(db.count(r)) return da[t] + db[r] + 1;if(da.count(r)) continue;da[r] = da[t] + 1;q.push(r);}}return 11; } int bfs(){if(A == B) return 0;queue<string> qa, qb;unordered_map<string, int> da, db;qa.push(A), qb.push(B);da[A] = db[B] = 0;int step = 0;while(qa.size() && qb.size()){int t;if(qa.size() < qb.size()) t = extend(qa, da, db, a, b);else t = extend(qb, db, da, b, a);if(t <= 10) return t;if(++step == 10) return -1;}return -1; } int main(){cin >> A >> B;while (cin >> a[n] >> b[n]) n ++ ;int t = bfs();if(t == -1) puts("NO ANSWER!");else cout << t << endl;return 0; }
import java.util.*;public class Main {public static void main(String[] args) {new Main().run();}final int N = 8;int n = 0;Queue<String> qa = new ArrayDeque<>(), qb = new ArrayDeque<>();//两个队列Map<String, Integer> ma = new HashMap<>(), mb = new HashMap<>();//存从a到中见状态,所需要的步数;存b到中间状态需要的步数。String[] a = new String[N], b = new String[N];//对应规则String A, B;/*** 从a到b进行扩展* @param q 队列* @param ma * @param mb* @param a* @param b* @return*/int extend(Queue<String> q, Map<String, Integer> ma, Map<String, Integer> mb, String[] a, String[] b){int d = ma.get(q.peek());for(int k = 0, len = q.size(); k < len; k++){String t = q.poll();for(int i = 0; i < t.length(); i++){for(int j = 0; j < n; j++){//我们首先看当前t中是否含有a中的字符串if(i + a[j].length() <= t.length() && t.substring(i, i + a[j].length()).equals(a[j])){String r = t.substring(0, i) + b[j] + t.substring(i + a[j].length());if(mb.containsKey(r)) return ma.get(t) + mb.get(r) + 1;if(ma.containsKey(r)) continue;ma.put(r, ma.get(t) + 1);//步数 + 1q.offer(r);}}}}return 11;}int bfs(){if(A.equals(B)) return 0;qa.offer(A);qb.offer(B);ma.put(A, 0);mb.put(B, 0);int step = 0;while(!qa.isEmpty() && !qb.isEmpty()){int t = 0;//若qa比较小我们就更新它if(qa.size() < qb.size()) t = extend(qa, ma, mb, a, b);else t = extend(qb, mb, ma, b, a);if(t <= 10) return t;if(++step == 10) return -1;}return -1;}void run(){Scanner sc = new Scanner(System.in);A = sc.next();B = sc.next();while(sc.hasNext()){a[n] = sc.next();b[n++] = sc.next();}sc.close();int ans = bfs();if(ans == -1) System.out.println("NO ANSWER!");else System.out.println(ans);} }
-
题目描述:给定一个5*5的棋盘,最终的位置应该是这样,左下角是白子,右上角是黑子。现在棋子可以通过跳日子形到空位上,问至少需要调多少次,才能使得所有棋子归为?(步数不超过15)
数据规模: n = 5
**分析:**这里显然是一道搜索题,且最终的步数不会超过15步,因此我们可以考虑使用迭代加深进行搜索。
优化:
- 我们可以加上一个估价函数进行剪枝。可以这样设计,将当前棋子中有多少个棋子未归为作为估价函数。满足当前步数 + 估价函数 <= 真实值
题解: 见分析
TIPS:
IDA*
时间复杂度
O(8^15)
#include<iostream>using namespace std; const int N = 5; int g[N][N]; int dest[N][N] = {{-1, -1, -1, -1, -1},{1, -1, -1, -1, -1},{1, 1, 0, -1, -1},{1, 1, 1, 1, -1},{1, 1, 1, 1, 1} }; int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1}; int n, depth; /*** 估价函数:还有多少棋子未归位* @return*/ int f(){int cnt = 0;for(int i = 0; i < N; i++)for(int j = 0; j < N; j++)if(g[i][j] && g[i][j] != dest[i][j]) cnt++;return cnt; } bool dfs(int u, int x, int y){if(u + f() > depth) return false;if(!f()) return true;for(int i = 0; i < 8; i++){int nx = x + dx[i], ny = y + dy[i];if(nx < 0 || nx >= N || ny < 0 || ny >= N) continue;swap(g[x][y], g[nx][ny]);if(dfs(u + 1, nx, ny)) return true;swap(g[x][y], g[nx][ny]);}return false; } int main(){cin >> n;while(n--){int x, y;for(int i = 0; i < N; i++){char s[5];scanf("%s", s);for(int j = 0; j < N; j++){if(s[j] == '1') g[i][j] = -1;else if(s[j] == '*') {g[i][j] = 0;x = i, y = j;}else g[i][j] = 1;}}depth = 0;while(depth <= 15 && !dfs(0, x, y)) depth++;if(depth > 15) puts("-1");else cout << depth << endl;} }
import java.util.Scanner;public class Main {public static void main(String[] args) {new Main().run();}final int N = 5;int[][] g = new int[N][N];int[][] dest = {{-1, -1, -1, -1, -1},{1, -1, -1, -1, -1},{1, 1, 0, -1, -1},{1, 1, 1, 1, -1},{1, 1, 1, 1, 1}};int[] dx = {-2, -1, 1, 2, 2, 1, -1, -2};int[] dy = {1, 2, 2, 1, -1, -2, -2, -1};int n, depth;/*** 股价函数* @return*/int f(){int cnt = 0;for(int i = 0; i < N; i++)for(int j = 0; j < N; j++)if(g[i][j] != 0 && g[i][j] != dest[i][j]) cnt++;return cnt;}boolean dfs(int u, int x, int y){if(u + f() > depth) return false;if(f() == 0) return true;for(int i = 0; i < 8; i++){int nx = x + dx[i], ny = y + dy[i];if(nx < 0 || nx >= N || ny < 0 || ny >= N) continue;g[x][y] = g[nx][ny];g[nx][ny] = 0;if(dfs(u + 1, nx, ny)) return true;g[nx][ny] = g[x][y];g[x][y] = 0;}return false;}void run(){Scanner sc = new Scanner(System.in);n = sc.nextInt();while(n-- > 0){int x = 0, y = 0;for(int i = 0; i < N; i++){char[] line = sc.next().toCharArray();for(int j = 0; j < N; j++){if(line[j] == '1') g[i][j] = -1;else if(line[j] == '0') g[i][j] = 1;else {x = i;y = j;}}}depth = 0;while(!dfs(0, x, y) && depth <= 15) depth++;if(depth <= 15) System.out.println(depth);else System.out.println(-1);}sc.close();} }
相关文章:

算法进阶-搜索
算法进阶-搜索 题目描述:给定一张N个点M条边的有向无环图,分别统计从每个点除法能够到达的点的数量 **数据规模:**1 < n < 3e4 **分析:**这里我们可以使用拓扑排序根据入边对所有点进行排序,排序后我们按照逆序&…...

时空智友企业流程化管控系统 sessionid泄露漏洞 复现
文章目录 时空智友企业流程化管控系统 sessionid泄露漏洞 复现0x01 前言0x02 漏洞描述0x03 影响平台0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 时空智友企业流程化管控系统 sessionid泄露漏洞 复现 0x01 前言 免责声明:请勿利用文章内的相关技术从…...

QT编程,QMainWindow、事件
目录 1、QMainWindow 2、事件 1、QMainWindow QMenuBar:菜单栏 QMenu: 菜单 QAction: 动作 QToolBar: 工具栏 QStatusBar: 状态栏 setWindowTitle("主窗口"); //: 前缀 文件名 setWindowIcon(QIcon(":/mw_images/10.png")); resize(640, 4…...

人工智能在教育上的应用2-基于大模型的未来数学教育的情况与实际应用
大家好,我是微学AI ,今天给大家介绍一下人工智能在教育上的应用2-基于大模型的未来数学教育的情况与实际应用,随着人工智能(AI)和深度学习技术的发展,大模型已经开始渗透到各个领域,包括数学教育。本文将详细介绍基于大模型在数学…...

C++学习day5
目录 作业: 1> 思维导图 2> 多继承代码实现沙发床 1>思维导图 2> 多继承代码实现沙发床 #include <iostream>using namespace std; //创建沙发类 class sofa { private:string sitting; public:sofa(){cout << "sofa的无参构造函数…...

1.软件开发-HTML结构-元素剖析
元素的嵌套 代码注释 ctrl/ URL url 统一资源定位符 一个给定的独特资源在web上的地址 URI...

QTableWidget 表格增删数据
QTableWidgetQTableWidgetQTableWidget部分使用方法,如在表格中插入或删除一行数据以及清空表格数据等。在添加数据时,设置了条件判断如正则表达式,若用户输入的数据不合法,则添加失败并提示用户错误的地方,便于用户修…...

Tableau:商业智能(BI)工具
Tableau入门 1、Tableau概述2、Tableau Desktop2.1、初识Tableau Desktop2.2、Tableau工作区2.3、数据窗格与分析窗格2.4、功能区和标记卡2.4.1、列和行功能区2.4.2、标记卡2.4.3、筛选器功能区2.4.4、页面功能区2.4.5、附加功能区、图例、控件 3、Tableau视图4、Tableau工作簿…...

【gmail注册教程】手把手教你注册Google邮箱账号
手把手教你注册Google邮箱账号 写在前面: 要注意,注册Google邮箱必须要确保自己能够 科学上网,如果暂时做不到,请先进行相关学习。使用的手机号是大陆(86)的。 在保证自己能够科学上网后,在浏…...

docker版jxTMS使用指南:数据采集系统的高可用性
本文讲解4.6版jxTMS中数据采集系统的高可用性,整个系列的文章请查看:4.6版升级内容 docker版本的使用,请查看:docker版jxTMS使用指南 4.0版jxTMS的说明,请查看:4.0版升级内容 4.2版jxTMS的说明ÿ…...

vue如何禁止通过页面输入路径跳转页面
要禁止通过页面输入路径跳转页面,你可以使用Vue Router的导航守卫(navigation guards)来拦截导航并阻止不希望的跳转。 下面是一种常见的方法,你可以在全局导航守卫(global navigation guards)中实现这个功…...

mac,linux环境的基础工具安装【jdk,tomcat】
安装 一 linux环境一)、JDK安装卸载: 二)、 tomcat 安装1、[下载](https://mirrors.bfsu.edu.cn/apache/tomcat/tomcat-8/v8.5.63/bin/apache-tomcat-8.5.63.tar.gz)后,在目录 /usr/local/tomcat下,解压缩2、配置tomca…...

chrome窗口
chrome 窗口的层次: 父窗口类名:Chrome_WidgetWin_1 有两个子窗口: Chrome_RenderWidgetHostHWNDIntermediate D3D Window // 用于匹配 Chrome 窗口的窗口类的前缀。 onst wchar_t kChromeWindowClassPrefix[] L"Chrome_WidgetWin_…...

某快递公司Java一面
1.平衡二叉树和红黑树的区别? 平衡二叉树是一种二叉搜索树,其左子树和右子树的高度差不超过1,以确保在最坏情况下的查找效率是O(log n)。而红黑树是一种自平衡二叉搜索树,通过引入颜色标记(红色和黑色)来维…...

【C++ Primer Plus学习记录】指针——声明和初始化指针
指针用于存储值的地址,因此,指针名表示的地址。*运算符被称为间接值或解除引用运算符,将其应用于指针,可以得到该地址处存储的值。 例如,假设manly是一个指针,则manly表示的是一个地址,而*manl…...

切换至root用户时,命令提示符颜色为白色,如何修改?
当我切换至root用户时,发现命令提示符里的内容全部为白色,如图所示: 这让人看着不愉快,上网先搜索下解决方案:【切换到 root 账户字体全是白的,怎么改颜色啊】- 百度贴吧,但是这个解决方案只是…...

设计模式——17. 状态模式
1. 说明 状态模式(State Pattern)是一种行为设计模式,它允许一个对象在其内部状态发生改变时改变其行为。状态模式将对象的状态封装成不同的状态对象,并将状态切换时的行为委托给当前状态对象。这样,对象在不同状态下具有不同的行为,而无需在对象本身中使用大量的条件语…...

系统架构设计:14 论软基于架构的软件设计方法(ABSD)的软件开发
目录 1 基于架构的软件设计(ABSD) 2 基于架构的软件开发过程 2.1 架构需求过程 2.2 架构设计过程</...

如何在 Spring Boot 中进行文件上传
在 Spring Boot 中进行文件上传 文件上传是Web应用程序中常见的功能之一,它允许用户将文件从客户端上传到服务器。Spring Boot提供了便捷的方式来处理文件上传,并且整合了Spring框架的强大功能,使文件上传变得相对简单。本文将介绍如何在Spr…...

Python 图形化界面基础篇:将应用程序打包为可执行文件
Python 图形化界面基础篇:将应用程序打包为可执行文件 引言 PyInstaller 简介步骤1:安装 PyInstaller 步骤2:创建 Python GUI 应用程序步骤3:使用 PyInstaller 打包应用程序 完整示例代码解释结论 引言 在开发完一个图形用户界面…...

Android 13.0 蓝牙遥控器确认键弹不出输入法的解决方法
1.概述 在android13.0设备定制化开发时,遥控器是使用红外遥控器,也有使用蓝牙遥控器的,所以出现的问题不一定相同,今天遇到个问题就是蓝牙遥控器在输入数据时弹不出输入法的问题 首选排除输入法的问题,安装其他的输入法,也是同样的问题,这样就确定是系统EditText控件相关…...

spring boot面试50问
目录 前言: 1. 什么是 Spring Boot? 2. 为什么要用SpringBoot? 3. SpringBoot与SpringCloud 区别? 4. Spring Boot 有哪些优点? 5. Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的࿱…...

条例24~25(设计与声明)
条例24 若所有参数皆需要类型转换,请为此采用非成员函数 有时候让类型内成员函数支持隐式类型转换是不妥善的。比如当我们想在类内实现operator *() 模拟乘法的时候。通常情况下表现良好,但若你想额外实现混合式运算。例如 int…...

Spring5应用之事务处理
作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者! 个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏:Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言事务…...

Python 中最常用的4种股票价格移动平均方法(三)
一、简介 移动平均线是各级交易者和投资者最广泛使用的技术指标之一。它们通过计算特定时期内的平均价格来帮助消除股票价格的固有波动性。移动平均线计算起来很简单,但也有更复杂的形式,旨在捕捉市场的更多细微差别。 这个由四部分组成的系列将讨论总共 4 种不同的移动平均方…...

Mybaits缓存踩的坑
记Mybaits缓存踩的坑 1.问题提出 最近开发一个记录操作前后修改内容的功能,获取修改前数据比较简单,直接从数据库获取,记录修改后的功能也比较简单,直接将用户修改的内容封装成po对象,然后两个比对就可以了ÿ…...

全国工商注册数据库的作用
随着经济的发展和市场竞争的加剧,越来越多的人开始关注公司的工商信息。这些信息不仅可以帮助人们了解公司的基本情况,还可以为投资者、合作伙伴、员工等提供决策依据。 工商数据库提供了全国范围内企业的基本信息。这些信息包括企业的名称、统一社会信用…...

【Linux】NTP时间服务器Chrony配置详解
🍁 博主 "开着拖拉机回家"带您 Go to New World.✨🍁 🦄 个人主页——🎐开着拖拉机回家_大数据运维-CSDN博客 🎐✨🍁 🪁🍁 希望本文能够给您带来一定的帮助🌸文…...

今年的秋招面试,确实有点难。
不可否认的是,今年秋招确实有点难 从今年的形势来看,好的 offer 都掌握在少数人的手里,想要秋招找到理想的工作,要么学历好,要么技术功底很扎实,这两样都不占的话,就业压力就会比较大。 如何从…...

Rn使用FlatList导航栏自动回到中间
import { useState, useRef } from react import { FlatList, View, Text, StyleSheet, TouchableOpacity } from react-nativeconst Center () > {const tabs ["语文", "数学", "英语", "政治", "历史", "地理&q…...