日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

AcWing算法提高课 Level-3 第二章 搜索

發布時間:2025/3/19 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AcWing算法提高课 Level-3 第二章 搜索 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


  • 池塘計數
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 農夫約翰有一片 N?M 的矩形土地。

    最近,由于降雨的原因,部分土地被水淹沒了。

    現在用一個字符矩陣來表示他的土地。

    每個單元格內,如果包含雨水,則用”W”表示,如果不含雨水,則用”.”表示。

    現在,約翰想知道他的土地中形成了多少片池塘。

    每組相連的積水單元格集合可以看作是一片池塘。

    每個單元格視為與其上、下、左、右、左上、右上、左下、右下八個鄰近單元格相連。

    請你輸出共有多少片池塘,即矩陣中共有多少片相連的”W”塊。

    輸入格式
    第一行包含兩個整數 N 和 M。

    接下來 N 行,每行包含 M 個字符,字符為”W”或”.”,用以表示矩形土地的積水狀況,字符之間沒有空格。

    輸出格式
    輸出一個整數,表示池塘數目。

    數據范圍
    1≤N,M≤1000
    輸入樣例:
    10 12
    W…WW.
    .WWW…WWW
    …WW…WW.
    …WW.
    …W…
    …W…W…
    .W.W…WW.
    W.W.W…W.
    .W.W…W.
    …W…W.
    輸出樣例:
    3

    //連通一般分為四連通(上下左右)和八連通(只要有公共點) #include <bits/stdc++.h>#define x first #define y secondusing namespace std;typedef pair<int, int> PII;const int N = 1010, M = N * N;int n, m; char g[N][N]; PII q[M]; bool st[N][N];void bfs(int sx, int sy) {int hh = 0, tt = 0;q[0] = {sx, sy};st[sx][sy] = 1;while (hh <= tt){PII t = q[hh ++ ];for (int i = t.x - 1; i <= t.x + 1; i ++ )for (int j = t.y - 1; j <= t.y + 1; j ++ ){if (i == t.x && j == t.y) continue;if (i < 0 || i >= n || j < 0 || j >= m) continue;if (g[i][j] == '.' || st[i][j]) continue;q[ ++ tt] = {i, j};st[i][j] = 1;}} }int main() {cin >> n >> m;for (int i = 0; i < n; i ++ ) scanf("%s", g[i]); //因為要讀1e3*1e3的數據,所以最好用scanfint cnt = 0; for (int i = 0; i < n; i ++ )for (int j = 0; j < m; j ++ ){if (g[i][j] == 'W' && !st[i][j]){bfs(i, j);cnt ++ ;}}cout << cnt << endl;return 0; }
  • 城堡問題
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 1 2 3 4 5 6 7

    #############################
    1 # | # | # | | #
    #####—#####—#---#####—#
    2 # # | # # # # #
    #—#####—#####—#####—#
    3 # | | # # # # #
    #—#########—#####—#---#
    4 # # | | | | # #
    #############################
    (圖 1)

    = Wall

    | = No wall

    • = No wall

    方向:上北下南左西右東。
    圖1是一個城堡的地形圖。

    請你編寫一個程序,計算城堡一共有多少房間,最大的房間有多大。

    城堡被分割成 m?n個方格區域,每個方格區域可以有0~4面墻。

    注意:墻體厚度忽略不計。

    輸入格式
    第一行包含兩個整數 m 和 n,分別表示城堡南北方向的長度和東西方向的長度。

    接下來 m 行,每行包含 n 個整數,每個整數都表示平面圖對應位置的方塊的墻的特征。

    每個方塊中墻的特征由數字 P 來描述,我們用1表示西墻,2表示北墻,4表示東墻,8表示南墻,P 為該方塊包含墻的數字之和。

    例如,如果一個方塊的 P 為3,則 3 = 1 + 2,該方塊包含西墻和北墻。

    城堡的內墻被計算兩次,方塊(1,1)的南墻同時也是方塊(2,1)的北墻。

    輸入的數據保證城堡至少有兩個房間。

    輸出格式
    共兩行,第一行輸出房間總數,第二行輸出最大房間的面積(方塊數)。

    數據范圍
    1≤m,n≤50,
    0≤P≤15
    輸入樣例:
    4 7
    11 6 11 6 3 10 6
    7 9 6 13 5 15 5
    1 10 12 7 13 7 5
    13 11 10 8 10 12 13
    輸出樣例:
    5
    9
    難度:簡單
    時/空限制:1s / 64MB
    總通過數:2377
    總嘗試數:3236
    來源:《信息學奧賽一本通》
    算法標簽

    #include <bits/stdc++.h>#define x first #define y secondusing namespace std;typedef pair<int, int> PII;const int N = 55, M = N * N;int n, m; int g[N][N]; PII q[M]; bool st[N][N];int bfs(int sx, int sy) {int dx[] = {0, -1, 0, 1}, dy[] = {-1, 0, 1, 0};int hh = 0, tt = 0;int area = 0;q[0] = {sx, sy};st[sx][sy] = 1;while (hh <= tt){PII t = q[hh ++ ];area ++ ;for (int i = 0; i < 4; i ++ ){int a = t.x + dx[i], b = t.y + dy[i];if (a < 0 || a >= n || b < 0 || b >= m) continue;if (st[a][b]) continue;// if (g[a][b] >> i & 1) continue; 是t.x而非a//1 2 4 8 -> 二進制中0 1 2 3位上是否為1if (g[t.x][t.y] >> i & 1) continue; //二進制中第i位是否為1,若為1則返回1st[a][b] = 1;// area ++ ; //不然的話main函數中。。。 整個area會少1q[ ++ tt] = {a, b};}}return area; }int main() {cin >> n >> m;for (int i = 0; i < n; i ++ )for (int j = 0; j < m; j ++ )cin >> g[i][j];int cnt = 0, area = 0;for (int i = 0; i < n; i ++ )for (int j = 0; j < m; j ++ ){if (!st[i][j]){cnt ++ ;area = max(area, bfs(i, j));}}cout << cnt << endl;cout << area << endl;return 0; }
  • 山峰和山谷
    題目
    提交記錄
    討論
    題解
    視頻講解
  • FGD小朋友特別喜歡爬山,在爬山的時候他就在研究山峰和山谷。

    為了能夠對旅程有一個安排,他想知道山峰和山谷的數量。

    給定一個地圖,為FGD想要旅行的區域,地圖被分為 n×n 的網格,每個格子 (i,j) 的高度 w(i,j) 是給定的。

    若兩個格子有公共頂點,那么它們就是相鄰的格子,如與 (i,j) 相鄰的格子有(i?1,j?1),(i?1,j),(i?1,j+1),(i,j?1),(i,j+1),(i+1,j?1),(i+1,j),(i+1,j+1)。

    我們定義一個格子的集合 S 為山峰(山谷)當且僅當:

    S 的所有格子都有相同的高度。
    S 的所有格子都連通。
    對于 s 屬于 S,與 s 相鄰的 s′ 不屬于 S,都有 ws>ws′(山峰),或者 ws<ws′(山谷)。
    如果周圍不存在相鄰區域,則同時將其視為山峰和山谷。
    你的任務是,對于給定的地圖,求出山峰和山谷的數量,如果所有格子都有相同的高度,那么整個地圖即是山峰,又是山谷。

    輸入格式
    第一行包含一個正整數 n,表示地圖的大小。

    接下來一個 n×n 的矩陣,表示地圖上每個格子的高度 w。

    輸出格式
    共一行,包含兩個整數,表示山峰和山谷的數量。

    數據范圍
    1≤n≤1000,
    0≤w≤109
    輸入樣例1:
    5
    8 8 8 7 7
    7 7 8 8 7
    7 7 7 7 7
    7 8 8 7 8
    7 8 8 8 8
    輸出樣例1:
    2 1
    輸入樣例2:
    5
    5 7 8 3 1
    5 5 7 6 6
    6 6 6 2 8
    5 7 2 5 8
    7 1 0 1 7
    輸出樣例2:
    3 3
    樣例解釋
    樣例1:

    1.png

    樣例2:

    2.png

    難度:中等
    時/空限制:1s / 64MB
    總通過數:2400
    總嘗試數:4415
    來源:《信息學奧賽一本通》 , POI2007
    算法標簽

    #include <bits/stdc++.h>#define x first #define y secondusing namespace std;typedef pair<int, int> PII;const int N = 1010, M = N * N;int n; int g[N][N]; PII q[M]; bool st[N][N];void bfs(int sx, int sy, bool &has_higher, bool &has_lower) //&可以改變main函數調用的值 {int hh = 0, tt = 0;q[0] = {sx, sy};st[sx][sy] = 1;while (hh <= tt){PII t = q[hh ++ ];for (int i = t.x - 1; i <= t.x + 1; i ++ )for (int j = t.y - 1; j <= t.y + 1; j ++ ){if (i == t.x && j == t.y) continue;if (i < 0 || i >= n || j < 0 || j >= n) continue;//如果高度不相等,不是連通塊,且根據高度得知周圍有比它高或者比它低判斷是否山谷或是否山峰if (g[i][j] != g[t.x][t.y]) {if (g[i][j] < g[t.x][t.y]) has_lower = 1;else has_higher = 1;}else if (!st[i][j]){q[ ++ tt] = {i, j};st[i][j] = 1;}}} }int main() {scanf("%d", &n);for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ )cin >> g[i][j];int peak = 0, valley = 0;for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ ){if (!st[i][j]){bool has_higher = 0, has_lower = 0;bfs(i, j, has_higher, has_lower);if (!has_higher) peak ++ ;if (!has_lower) valley ++ ; //不能使用else if,因為可能既是山谷也是山峰}}printf("%d %d\n", peak, valley); }
  • 迷宮問題
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 給定一個 n×n 的二維數組,如下所示:

    int maze[5][5] = {

    0, 1, 0, 0, 0,

    0, 1, 0, 1, 0,

    0, 0, 0, 0, 0,

    0, 1, 1, 1, 0,

    0, 0, 0, 1, 0,

    };
    它表示一個迷宮,其中的1表示墻壁,0表示可以走的路,只能橫著走或豎著走,不能斜著走,要求編程序找出從左上角到右下角的最短路線。

    數據保證至少存在一條從左上角走到右下角的路徑。

    輸入格式
    第一行包含整數 n。

    接下來 n 行,每行包含 n 個整數 0 或 1,表示迷宮。

    輸出格式
    輸出從左上角到右下角的最短路線,如果答案不唯一,輸出任意一條路徑均可。

    按順序,每行輸出一個路徑中經過的單元格的坐標,左上角坐標為 (0,0),右下角坐標為 (n?1,n?1)。

    數據范圍
    0≤n≤1000
    輸入樣例:
    5
    0 1 0 0 0
    0 1 0 1 0
    0 0 0 0 0
    0 1 1 1 0
    0 0 0 1 0
    輸出樣例:
    0 0
    1 0
    2 0
    2 1
    2 2
    2 3
    2 4
    3 4
    4 4

    #include <bits/stdc++.h>#define x first #define y secondusing namespace std;typedef pair<int, int> PII;const int N = 1010, M = N * N;int n; int g[N][N]; PII q[M]; PII pre[N][N];void bfs(int sx, int sy) {memset(pre, -1, sizeof(pre)); //初始化pre數組,定義一個不能到達的值為未訪問int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};int hh = 0, tt = 0;q[0] = {sx, sy};pre[sx][sy] = {0, 0};while (hh <= tt){PII t = q[hh ++ ];for (int i = 0; i < 4; i ++ ){int a = t.x + dx[i], b = t.y + dy[i];if (a < 0 || a >= n || b < 0 || b >= n) continue;if (g[a][b]) continue;if (pre[a][b].x != -1) continue; //不能寫pre[a][b] != -1q[ ++ tt] = {a, b};pre[a][b] = t;}} }int main() {cin >> n;for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ )scanf("%d", &g[i][j]);bfs(n - 1, n - 1); //從終點開始遍歷,為了prePII end = {0, 0}; //定義一個pair類型變量end方便遍歷pre數組while (1){printf("%d %d\n", end.x, end.y);if (end.x == n - 1 && end.y == n - 1) break;end = pre[end.x][end.y];}return 0; }
  • 武士風度的牛
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 農民 John 有很多牛,他想交易其中一頭被 Don 稱為 The Knight 的牛。

    這頭牛有一個獨一無二的超能力,在農場里像 Knight 一樣地跳(就是我們熟悉的象棋中馬的走法)。

    雖然這頭神奇的牛不能跳到樹上和石頭上,但是它可以在牧場上隨意跳,我們把牧場用一個 x,y 的坐標圖來表示。

    這頭神奇的牛像其它牛一樣喜歡吃草,給你一張地圖,上面標注了 The Knight 的開始位置,樹、灌木、石頭以及其它障礙的位置,除此之外還有一捆草。

    現在你的任務是,確定 The Knight 要想吃到草,至少需要跳多少次。

    The Knight 的位置用 K 來標記,障礙的位置用 * 來標記,草的位置用 H 來標記。

    這里有一個地圖的例子:

    11 | . . . . . . . . . .10 | . . . . * . . . . . 9 | . . . . . . . . . . 8 | . . . * . * . . . . 7 | . . . . . . . * . . 6 | . . * . . * . . . H 5 | * . . . . . . . . . 4 | . . . * . . . * . . 3 | . K . . . . . . . . 2 | . . . * . . . . . * 1 | . . * . . . . * . . 0 ----------------------1 0 1 2 3 4 5 6 7 8 9 0

    The Knight 可以按照下圖中的 A,B,C,D… 這條路徑用 5 次跳到草的地方(有可能其它路線的長度也是 5):

    11 | . . . . . . . . . .10 | . . . . * . . . . .9 | . . . . . . . . . .8 | . . . * . * . . . .7 | . . . . . . . * . .6 | . . * . . * . . . F<5 | * . B . . . . . . .4 | . . . * C . . * E .3 | .>A . . . . D . . .2 | . . . * . . . . . *1 | . . * . . . . * . .0 ----------------------10 1 2 3 4 5 6 7 8 9 0

    注意: 數據保證一定有解。

    輸入格式
    第 1 行: 兩個數,表示農場的列數 C 和行數 R。

    第 2…R+1 行: 每行一個由 C 個字符組成的字符串,共同描繪出牧場地圖。

    輸出格式
    一個整數,表示跳躍的最小次數。

    數據范圍
    1≤R,C≤150
    輸入樣例:
    10 11

    ……

    ….…
    ……
    ………H

    ………
    .K…
    ……*
    ………
    輸出樣例:
    5

    #include <bits/stdc++.h> #define endl '\n' #define x first #define y second using namespace std; typedef pair<int, int> PII; const int N = 155, M = N * N;int n, m; char g[N][N]; int dist[N][N]; PII q[M];int bfs() {memset(dist, -1, sizeof(dist));int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2}, dy[] = {1, 2, 2, 1, -1, -2, -2, -1};int sx, sy;for (int i = 0; i < n; i ++ )for (int j = 0; j < m; j ++ )if (g[i][j] == 'K')sx = i, sy = j;int hh = 0, tt = 0;q[0] = {sx, sy};dist[sx][sy] = 0;while (hh <= tt){PII t = q[hh ++ ];for (int i = 0; i < 8; i ++ ){int a = t.x + dx[i], b = t.y + dy[i];if (a < 0 || a >= n || b < 0 || b >= m) continue;if (g[a][b] == '*') continue;if (dist[a][b] != -1) continue;if (g[a][b] == 'H') return dist[t.x][t.y] + 1;dist[a][b] = dist[t.x][t.y] + 1;q[ ++ tt] = {a, b};}}return -1; }int main() {// cin >> n >> m;cin >> m >> n;for (int i = 0; i < n; i ++ ) cin >> g[i];cout << bfs() << endl; }
  • 抓住那頭牛
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 農夫知道一頭牛的位置,想要抓住它。

    農夫和牛都位于數軸上,農夫起始位于點 N,牛位于點 K。

    農夫有兩種移動方式:

    從 X 移動到 X?1 或 X+1,每次移動花費一分鐘
    從 X 移動到 2?X,每次移動花費一分鐘
    假設牛沒有意識到農夫的行動,站在原地不動。

    農夫最少要花多少時間才能抓住牛?

    輸入格式
    共一行,包含兩個整數N和K。

    輸出格式
    輸出一個整數,表示抓到牛所花費的最少時間。

    數據范圍
    0≤N,K≤105
    輸入樣例:
    5 17
    輸出樣例:
    4

    //所有邊的權重相同都為1,就可以用bfs求出最短路徑 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N = 1e5 + 10; //范圍 waint n, k; int q[N]; int dist[N];int bfs() {memset(dist, -1, sizeof dist);dist[n] = 0;q[0] = n;int hh = 0, tt = 0;while (hh <= tt){int t = q[hh ++ ];if (t == k) return dist[k];if (t + 1 < N && dist[t + 1] == -1){dist[t + 1] = dist[t] + 1;q[ ++ tt] = t + 1;}if (t - 1 >= 0 && dist[t - 1] == -1){dist[t - 1] = dist[t] + 1;q[ ++ tt] = t - 1;}if (t * 2 < N && dist[t * 2] == -1){dist[t * 2] = dist[t] + 1;q[ ++ tt] = t * 2;}}return -1; }int main() {cin >> n >> k;cout << bfs() << endl;return 0; }
  • 矩陣距離
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 給定一個 N 行 M 列的 01 矩陣 A,A[i][j] 與 A[k][l] 之間的曼哈頓距離定義為:

    dist(A[i][j],A[k][l])=|i?k|+|j?l|
    輸出一個 N 行 M 列的整數矩陣 B,其中:

    B[i][j]=min1≤x≤N,1≤y≤M,A[x][y]=1dist(A[i][j],A[x][y])
    輸入格式
    第一行兩個整數 N,M。

    接下來一個 N 行 M 列的 01 矩陣,數字之間沒有空格。

    輸出格式
    一個 N 行 M 列的矩陣 B,相鄰兩個整數之間用一個空格隔開。

    數據范圍
    1≤N,M≤1000
    輸入樣例:
    3 4
    0001
    0011
    0110
    輸出樣例:
    3 2 1 0
    2 1 0 0
    1 0 0 1
    難度:簡單
    時/空限制:1s / 64MB
    總通過數:3432
    總嘗試數:5652
    來源:《算法競賽進階指南》, 小馬智行面試題
    算法標簽

    //樸素想法:有一個起點的時候。。。 有多個起點:把每個1分別放在隊首,遍歷一遍,保留最小值,O(n^2),鐵T //圖論中求一個點到其他最近的點的最短距離,與多源最短路(任意兩點最短距離)不一樣,可以轉換為單源最短路//多源 1.兩端性 2.單調性 e.g x x x x x ... x+1 x+1 x+1 ...//本題輸入的時候是一串連在一起的數字,中間沒有空格相隔,所以要用字符串讀,不能用int //1e3 * 1e3 的 char 類型輸入 -> 1e6的字節 只占1MB空間,不算大 #include <bits/stdc++.h> #define end '\n' #define x first #define y second using namespace std; typedef pair<int, int> PII; const int N = 1010, M = N * N;int n, m; char g[N][N]; PII q[M]; int dist[N][N];void bfs() { memset(dist, -1, sizeof(dist));int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};int hh = 0, tt = -1; // wa!!!for (int i = 1; i <= n; i ++ )for (int j = 1; j <= m; j ++ )if (g[i][j] == '1'){dist[i][j] = 0;q[ ++ tt] = {i, j};}while (hh <= tt){PII t = q[hh ++ ];for (int i = 0; i < 4; i ++ ){int a = t.x + dx[i], b = t.y + dy[i];if (a <= 0 || a > n || b <= 0 || b > m) continue;if (dist[a][b] != -1) continue;dist[a][b] = dist[t.x][t.y] + 1;q[ ++ tt] = {a, b};}} }int main() {cin >> n >> m;for (int i = 1; i <= n; i ++ ) scanf("%s", g[i] + 1);bfs();for (int i = 1; i <= n; i ++ ){for (int j = 1; j <= m; j ++ ){printf("%d ", dist[i][j]); // %d而非%c wa}puts("");}return 0; }
  • 魔板
    題目
    提交記錄
    討論
    題解
    視頻講解
  • Rubik 先生在發明了風靡全球的魔方之后,又發明了它的二維版本——魔板。

    這是一張有 8 個大小相同的格子的魔板:

    1 2 3 4
    8 7 6 5
    我們知道魔板的每一個方格都有一種顏色。

    這 8 種顏色用前 8 個正整數來表示。

    可以用顏色的序列來表示一種魔板狀態,規定從魔板的左上角開始,沿順時針方向依次取出整數,構成一個顏色序列。

    對于上圖的魔板狀態,我們用序列 (1,2,3,4,5,6,7,8) 來表示,這是基本狀態。

    這里提供三種基本操作,分別用大寫字母 A,B,C 來表示(可以通過這些操作改變魔板的狀態):

    A:交換上下兩行;
    B:將最右邊的一列插入到最左邊;
    C:魔板中央對的4個數作順時針旋轉。

    下面是對基本狀態進行操作的示范:

    A:

    8 7 6 5
    1 2 3 4
    B:

    4 1 2 3
    5 8 7 6
    C:

    1 7 2 4
    8 6 3 5
    對于每種可能的狀態,這三種基本操作都可以使用。

    你要編程計算用最少的基本操作完成基本狀態到特殊狀態的轉換,輸出基本操作序列。

    注意:數據保證一定有解。

    輸入格式
    輸入僅一行,包括 8 個整數,用空格分開,表示目標狀態。

    輸出格式
    輸出文件的第一行包括一個整數,表示最短操作序列的長度。

    如果操作序列的長度大于0,則在第二行輸出字典序最小的操作序列。

    數據范圍
    輸入數據中的所有數字均為 1 到 8 之間的整數。

    輸入樣例:
    2 6 8 4 5 7 3 1
    輸出樣例:
    7
    BCABCCB
    難度:簡單
    時/空限制:1s / 64MB
    總通過數:1970
    總嘗試數:3199
    來源:《信息學奧賽一本通》 , usaco training 3.2
    算法標簽

    //最小步數bfs模型(八數碼)里,經常用哈希法存狀態,而c++里可以用map或umap做哈希 #include <bits/stdc++.h>using namespace std;char g[2][4]; //不要int wa,因為是將它與字符串進行操作 unordered_map<string, int> dist; unordered_map<string, pair<char, string>> pre; queue<string> q;void build(string state) {for (int i = 0; i < 4; i ++ ) g[0][i] = state[i];for (int j = 3, i = 4; j >= 0; j -- , i ++ ) g[1][j] = state[i]; }string get() {string ans;for (int i = 0; i < 4; i ++ ) ans += g[0][i];for (int i = 3; i >= 0; i-- ) ans += g[1][i];return ans; }string move0(string state) {build(state);for (int i = 0; i < 4; i ++ ) swap(g[0][i], g[1][i]);return get(); }string move1(string state) {build(state);int v0 = g[0][3], v1 = g[1][3];for (int i = 0; i < 2; i ++ )for (int j = 3; j >= 0; j -- )g[i][j] = g[i][j - 1];g[0][0] = v0, g[1][0] = v1;return get(); }string move2(string state) {build(state);int v = g[0][1];g[0][1] = g[1][1];g[1][1] = g[1][2];g[1][2] = g[0][2];g[0][2] = v;return get(); }int bfs(string start, string end) {if (start == end) return 0;dist[start] = 0;q.push(start);while (q.size()){auto t = q.front();q.pop();string m[3];m[0] = move0(t);m[1] = move1(t);m[2] = move2(t);for (int i = 0; i < 3; i ++ ){if (!dist[m[i]]){dist[m[i]] = dist[t] + 1;pre[m[i]] = {'A' + i, t};if (m[i] == end) return dist[end];q.push(m[i]);}}}return -1; }int main() {int x;string start, end;for (int i = 0; i < 8; i ++ ){int x;cin >> x;end += char(x + '0');}for (int i = 0; i < 8; i ++ ) start += char(i + '1');int step = bfs(start, end);cout << step << endl;string res;while (end != start){res += pre[end].first;end = pre[end].second;}reverse(res.begin(), res.end());if (step > 0) cout << res << endl;return 0; }
  • 電路維修
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 達達是來自異世界的魔女,她在漫無目的地四處漂流的時候,遇到了善良的少女翰翰,從而被收留在地球上。

    翰翰的家里有一輛飛行車。

    有一天飛行車的電路板突然出現了故障,導致無法啟動。

    電路板的整體結構是一個 R 行 C 列的網格(R,C≤500),如下圖所示。

    電路.png

    每個格點都是電線的接點,每個格子都包含一個電子元件。

    電子元件的主要部分是一個可旋轉的、連接一條對角線上的兩個接點的短電纜。

    在旋轉之后,它就可以連接另一條對角線的兩個接點。

    電路板左上角的接點接入直流電源,右下角的接點接入飛行車的發動裝置。

    達達發現因為某些元件的方向不小心發生了改變,電路板可能處于斷路的狀態。

    她準備通過計算,旋轉最少數量的元件,使電源與發動裝置通過若干條短纜相連。

    不過,電路的規模實在是太大了,達達并不擅長編程,希望你能夠幫她解決這個問題。

    注意:只能走斜向的線段,水平和豎直線段不能走。

    輸入格式
    輸入文件包含多組測試數據。

    第一行包含一個整數 T,表示測試數據的數目。

    對于每組測試數據,第一行包含正整數 R 和 C,表示電路板的行數和列數。

    之后 R 行,每行 C 個字符,字符是"/“和”"中的一個,表示標準件的方向。

    輸出格式
    對于每組測試數據,在單獨的一行輸出一個正整數,表示所需的縮小旋轉次數。

    如果無論怎樣都不能使得電源和發動機之間連通,輸出 NO SOLUTION。

    數據范圍
    1≤R,C≤500,
    1≤T≤5
    輸入樣例:
    1
    3 5
    \/\
    \///
    /\\
    輸出樣例:
    1
    樣例解釋
    樣例的輸入對應于題目描述中的情況。

    只需要按照下面的方式旋轉標準件,就可以使得電源和發動機之間連通。

    電路2.png

    //相當于給了一個無向圖,這個無向圖中只有兩種邊,第一種邊邊權是0,第二種邊權是1(前面是所有邊邊權都是1,可以直接BFS求它最短路),且僅包含01邊權 //問在這樣的圖上,從起點到終點的最短路徑是多少 ,可以用dijkstra做(dijk要求其實是在非負權值圖上做)//因為沿斜線走的,一開始橫縱坐標之和是偶數,所以之后也都是偶數,所以是之和是奇數的點一定到不了//把邊權為0的拓展到隊首,邊權為1的拓展到隊尾,仍滿足兩段性和單調性 x x .. x || x x .. x || x+1 x+1 .. x+1 || x+2 x+2 .. x+2 //每個點可能會被更新入隊多次(di=dj, di+1>dj+0),和一般bfs(d<=d_ -> d+1 <= d_+1,前面更新的肯定小于等于后面更新的,所以每個點只會入隊一次)不一樣,本質上dijk//每個點第一次被拓展到時不一定是最優解,只有這個點第一次從堆中出來的時候,也就是當前點是當前堆中最小值時,它的值才一定是最優解,這就是一個簡化版的dijk #include <bits/stdc++.h>using namespace std;typedef pair<int, int> PII;const int N = 550, M = N * N;int n, m; char g[N][N]; int dist[N][N]; bool st[N][N];int bfs() {deque<PII> q;memset(st, 0, sizeof(st));memset(dist, 0x3f, sizeof(dist)); //dist初始化不再是-1,因為需要更新最小值char cs[5] = "\\/\\/"; // 轉義字符 4 -> 5 注意順序是順時針(和下面dx, ix都是對應的)數過來那個點周圍的四個方格的int dx[] = {-1, -1, 1, 1}, dy[] = {-1, 1, 1, -1};int ix[] = {-1, -1, 0, 0}, iy[] = {-1, 0, 0, -1};q.push_back({0, 0});dist[0][0] = 0;while (q.size()){auto t = q.front();q.pop_front();int x = t.first, y = t.second;if (x == n && y == m) return dist[n][m];if (st[x][y]) continue;st[x][y] = 1;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) continue; //輸入進的是方格個數,而格點是[0,n]int ga = x + ix[i], gb = y + iy[i];int w = (g[ga][gb] != cs[i]);int d = dist[x][y] + w;if (d <= dist[a][b]){dist[a][b] = d;if (!w) q.push_front({a, b});else q.push_back({a, b});}}}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]); //和下一條的先后順序,即使一開始就判斷出答案,也要先輸入if (n + m & 1) puts("NO SOLUTION");else printf("%d\n", bfs());}return 0; }
  • 字串變換
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 已知有兩個字串 A, B 及一組字串變換的規則(至多 6 個規則):

    A1→B1
    A2→B2

    規則的含義為:在 A 中的子串 A1 可以變換為 B1、A2 可以變換為 B2…。

    例如:A=abcd B=xyz

    變換規則為:

    abc → xu ud → y y → yz

    則此時,A 可以經過一系列的變換變為 B,其變換的過程為:

    abcd → xud → xy → xyz

    共進行了三次變換,使得 A 變換為 B。

    輸入格式
    輸入格式如下:

    A B
    A1 B1
    A2 B2
    … …

    第一行是兩個給定的字符串 A 和 B。

    接下來若干行,每行描述一組字串變換的規則。

    所有字符串長度的上限為 20。

    輸出格式
    若在 10 步(包含 10 步)以內能將 A 變換為 B ,則輸出最少的變換步數;否則輸出 NO ANSWER!。

    輸入樣例:
    abcd xyz
    abc xu
    ud y
    y yz
    輸出樣例:
    3

    //即使只有6種規則,一次op分支是很多的,復雜度很高,所以要a和b相互找對方 //復雜度 e.g k^10 -> 2*k^5 指數級變化 搜索需要的狀態量面積變為原先的比如2/k^5//搜索寬度太寬時(單向超時時)用雙向//雙向bfs搜時, 兩邊每次擴展的時候必須把一整層擴展出來,否則wa,不能每次只擴展一個點 #include <bits/stdc++.h> using namespace std;const int N = 10;string A, B; int n; 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 < (int)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());//substr只傳一個參數表示從這個位置到末尾的子串if (db.count(r)) return da[t] + 1 + db[r];if (da.count(r)) continue;da[r] = da[t] + 1;q.push(r);}}}return 11; }int bfs() {queue<string> qa, qb;unordered_map<string, int> da, db;da[A] = 0, db[B] = 0;qa.push(A), qb.push(B);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 == 11) 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 printf("%d\n", t);return 0; } //2.2.1 DFS中的連通性和搜索順序//有些問題既可以深搜做也可以寬搜 基于連通性的模型的問題 比如flood-fill模型,或有關于圖和樹的遍歷 ///它們都有個特點,判斷從一個點能否走到某個點,是在棋盤內部做的,包括樹的遍歷也是,,是一個局部信息//dfs的另一類題一般都是看成一個整體,問能否從這一個整體變成另一個整體,是把一個整體看出一個點,
  • 迷宮
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 一天Extense在森林里探險的時候不小心走入了一個迷宮,迷宮可以看成是由 n?n 的格點組成,每個格點只有2種狀態,.和#,前者表示可以通行后者表示不能通行。

    同時當Extense處在某個格點時,他只能移動到東南西北(或者說上下左右)四個方向之一的相鄰格點上,Extense想要從點A走到點B,問在不走出迷宮的情況下能不能辦到。

    如果起點或者終點有一個不能通行(為#),則看成無法辦到。

    注意:A、B不一定是兩個不同的點。

    輸入格式
    第1行是測試數據的組數 k,后面跟著 k 組輸入。

    每組測試數據的第1行是一個正整數 n,表示迷宮的規模是 n?n 的。

    接下來是一個 n?n 的矩陣,矩陣中的元素為.或者#。

    再接下來一行是 4 個整數 ha,la,hb,lb,描述 A 處在第 ha 行, 第 la 列,B 處在第 hb 行, 第 lb 列。

    注意到 ha,la,hb,lb 全部是從 0 開始計數的。

    輸出格式
    k行,每行輸出對應一個輸入。

    能辦到則輸出“YES”,否則輸出“NO”。

    數據范圍
    1≤n≤100
    輸入樣例:
    2
    3
    .##
    …#
    #…
    0 0 2 2
    5

    ###.#
    …#…
    ###…
    …#.
    0 0 4 0
    輸出樣例:
    YES
    NO

    //深搜解迷宮問題局限性:深搜只能求出能否連通,不能保證第一次到時最短//每個點最多只被遍歷到一次,所以復雜度是線性的//這里的dfs不需要回溯 因為這里的狀態是格子,每個格子不需要被重復搜索,所以不需要恢復原狀//求連通性問題不需要回溯? 因為遍歷后不需要再從那點開始遍歷;以單個格子為狀態 #include <bits/stdc++.h> #define endl '\n' using namespace std; typedef long long ll;const int maxn = 110;char g[maxn][maxn]; bool st[maxn][maxn]; int n; int ax, ay, bx, by;int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};bool dfs(int x, int y) {if (g[x][y] == '#') return 0;if (x == bx && y == by) return 1;for (int i = 0; i < 4; ++ i){int a = x + dx[i], b = y + dy[i];if (a < 0 || a >= n || b < 0 || b >= n) continue;if (st[a][b]) continue;st[a][b] = 1;if (dfs(a, b)) return 1;}return 0; }int main() {int T;cin >> T;while (T--){cin >> n;for (int i = 0; i < n; ++ i) cin >> g[i];memset(st, 0, sizeof st);cin >> ax >> ay >> bx >> by;if (dfs(ax, ay)) puts("YES");else puts("NO");}return 0; } //深搜的兩大類模型//從人體內部的一個點到另一個點(棋盤的一個點到棋盤的另一個點),為了保證每個點只被搜一次,不能恢復現場//但如果把整個棋盤看成一個狀態,當搜到一個分支后,一定要把棋盤恢復原狀才可以搜下一個分支 回溯
  • 紅與黑
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 有一間長方形的房子,地上鋪了紅色、黑色兩種顏色的正方形瓷磚。

    你站在其中一塊黑色的瓷磚上,只能向相鄰(上下左右四個方向)的黑色瓷磚移動。

    請寫一個程序,計算你總共能夠到達多少塊黑色的瓷磚。

    輸入格式
    輸入包括多個數據集合。

    每個數據集合的第一行是兩個整數 W 和 H,分別表示 x 方向和 y 方向瓷磚的數量。

    在接下來的 H 行中,每行包括 W 個字符。每個字符表示一塊瓷磚的顏色,規則如下

    1)‘.’:黑色的瓷磚;
    2)‘#’:紅色的瓷磚;
    3)‘@’:黑色的瓷磚,并且你站在這塊瓷磚上。該字符在每個數據集合中唯一出現一次。

    當在一行中讀入的是兩個零時,表示輸入結束。

    輸出格式
    對每個數據集合,分別輸出一行,顯示你從初始位置出發能到達的瓷磚數(記數時包括初始位置的瓷磚)。

    數據范圍
    1≤W,H≤20
    輸入樣例:
    6 9
    …#.
    …#





    #@…#
    .#…#.
    0 0
    輸出樣例:
    45
    難度:簡單
    時/空限制:1s / 64MB
    總通過數:9374
    總嘗試數:16300
    來源:《信息學奧賽一本通》
    算法標簽

    //flood-fill 用bfs或dfs均可,只是dfs有爆棧的風險 1M -> 256M#include <bits/stdc++.h> #define endl '\n' #define x first #define y second using namespace std; typedef long long ll; typedef pair<int, int> PII;const int maxn = 25;int n, m; char g[maxn][maxn]; int res = 0;int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};void bfs(int x, int y) {queue<PII> q;q.push({x, y});while (q.size()){auto t = q.front();q.pop();res ++ ;for (int i = 0; i < 4; ++ i){int a = t.x + dx[i], b = t.y + dy[i];if (a < 0 || a >= n || b < 0 || b >= m) continue;if (g[a][b] != '.') continue;g[a][b] = '#';q.push({a, b});}} }int main() {while (cin >> m >> n, n || m){for (int i = 0; i < n; ++ i) cin >> g[i];res = 0;int x, y;for (int i = 0; i < n; ++ i)for (int j = 0; j < m; ++ j){if (g[i][j] == '@')x = i, y = j;}bfs(x, y);cout << res << endl;}return 0; } #include <bits/stdc++.h> #define endl '\n' using namespace std; typedef long long ll; const int maxn = 25;int n, m; char g[maxn][maxn];int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};int dfs(int x, int y) {int res = 1;g[x][y] = '#';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) continue;if (g[a][b] != '.') continue;res += dfs(a, b);}return res; }int main() {while (cin >> m >> n, n || m){for (int i = 0; i < n; ++ i) cin >> g[i];int x, y;for (int i = 0; i < n; ++ i)for (int j = 0; j < m; ++ j)if (g[i][j] == '@')x = i, y = j;cout << dfs(x, y) << endl;}return 0; }

    //后面的外部搜索//求最值,求數量 和dp問題一樣,但爆搜比較簡單,dp一個集合
  • 馬走日
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 馬在中國象棋以日字形規則移動。

    請編寫一段程序,給定 n?m 大小的棋盤,以及馬的初始位置 (x,y),要求不能重復經過棋盤上的同一個點,計算馬可以有多少途徑遍歷棋盤上的所有點。

    輸入格式
    第一行為整數 T,表示測試數據組數。

    每一組測試數據包含一行,為四個整數,分別為棋盤的大小以及初始位置坐標 n,m,x,y。

    輸出格式
    每組測試數據包含一行,為一個整數,表示馬能遍歷棋盤的途徑總數,若無法遍歷棋盤上的所有點則輸出 0。

    數據范圍
    1≤T≤9,
    1≤m,n≤9,
    0≤x≤n?1,
    0≤y≤m?1
    輸入樣例:
    1
    5 4 0 0
    輸出樣例:
    32

    //搜索樹//這題是,搜索樹中,每個狀態都是一個節點,每變化一步,都變一下變一下,它每個兒子都是把父節點復制過去變一下//恢復現場 下去是什么樣回來就是什么樣 所以這里不需要st初始化,因為回來的時候和下去時候一樣全是0//一般不會去問dfs的時間復雜度,一般認為是指數的,,除非是內部搜索,每個點只搜一次,所以是線性的//這是個外部搜索 所以需要恢復現場(相當于是否需要回溯?//將st變為0或1,放外面而不放for內部的話,可以減少運算,快一點//搜索結束就是把點搜完 所以再傳一個參數,當前在搜第幾個點 #include <bits/stdc++.h> #define endl '\n' using namespace std; typedef long long ll; const int N = 15;int n, m; bool st[N][N]; int ans;int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2}, dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};void dfs(int x, int y, int cnt) {if (cnt == n * m){ans ++ ;return ;}st[x][y] = 1;for (int i = 0; i < 8; i ++ ){int a = x + dx[i], b = y + dy[i];if (a < 0 || a >= n || b < 0 || b >= m) continue;if (st[a][b]) continue;dfs(a, b, cnt + 1);}st[x][y] = 0; }int main() {int T;cin >> T;while (T--){int x, y;cin >> n >> m >> x >> y;ans = 0;dfs(x, y, 1);cout << ans << endl;}return 0; } #include <bits/stdc++.h> #define endl '\n' using namespace std; typedef long long ll; const int N = 15;int n, m; bool st[N][N]; int ans, cnt;int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2}, dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};void dfs(int x, int y) {if (cnt == n * m){ans ++ ;return ;}st[x][y] = 1;cnt ++ ;for (int i = 0; i < 8; i ++ ){int a = x + dx[i], b = y + dy[i];if (a < 0 || a >= n || b < 0 || b >= m) continue;if (st[a][b]) continue;dfs(a, b);}st[x][y] = 0;cnt -- ; }int main() {int T;cin >> T;while (T--){int x, y;cin >> n >> m >> x >> y;ans = 0;cnt = 1;dfs(x, y);cout << ans << endl;}return 0; } #include <bits/stdc++.h> using namespace std; const int N = 15;int n, m, ans; bool st[N][N];int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2}, dy[] = {1, 2, 2, 1, -1, -2, -2, -1};void dfs(int x, int y, int cnt) {// st[x][y] = 1; 都可以if (cnt == n * m){ans ++ ;// return ; 這里不能return不然的話就沒辦法恢復狀態了}st[x][y] = 1;for (int i = 0; i < 8; i ++ ){int a = x + dx[i], b = y + dy[i];if (a < 0 || a >= n || b < 0 || b >= m) continue;if (st[a][b]) continue;dfs(a, b, cnt + 1);}st[x][y] = 0; }int main() {int T;scanf("%d", &T);while (T--){scanf("%d%d", &n, &m);int x, y;scanf("%d%d", &x, &y);ans = 0;dfs(x, y, 1);cout << ans << endl;}return 0; }
  • 單詞接龍
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 單詞接龍是一個與我們經常玩的成語接龍相類似的游戲。

    現在我們已知一組單詞,且給定一個開頭的字母,要求出以這個字母開頭的最長的“龍”,每個單詞最多被使用兩次。

    在兩個單詞相連時,其重合部分合為一部分,例如 beast 和 astonish ,如果接成一條龍則變為 beastonish。

    我們可以任意選擇重合部分的長度,但其長度必須大于等于1,且嚴格小于兩個串的長度,例如 at 和 atide 間不能相連。

    輸入格式
    輸入的第一行為一個單獨的整數 n 表示單詞數,以下 n 行每行有一個單詞(只含有大寫或小寫字母,長度不超過20),輸入的最后一行為一個單個字符,表示“龍”開頭的字母。

    你可以假定以此字母開頭的“龍”一定存在。

    輸出格式
    只需輸出以此字母開頭的最長的“龍”的長度。

    數據范圍
    n≤20
    輸入樣例:
    5
    at
    touch
    cheat
    choose
    tact
    a
    輸出樣例:
    23
    提示
    連成的“龍”為 atoucheatactactouchoose。

    //表述“每個單詞最多被使用兩次“和”每個單詞最多在龍中出現兩次“的區別,包含,一個單詞在另一個單詞中出現,kmp算法 #include <bits/stdc++.h> #define endl '\n' using namespace std; typedef long long ll; const int N = 25;string word[N]; int g[N][N]; int used[N]; int ans, n;void dfs(string dragon, int last) {ans = max(ans, (int)dragon.size()); //max函數里兩個參數必須同一類型used[last] ++ ; //雖說last,其實是當前字符串的下標for (int i = 0; i < n; i ++ )if (g[last][i] && used[i] < 2)//與重合長度有關來推公式dfs(dragon + word[i].substr(g[last][i]), i); //substr只傳一個參數表示從這個位置開始到末尾的子串,用來求后綴used[last] -- ; }int main() {cin >> n;char start;for (int i = 0; i < n; i ++ ) cin >> word[i];cin >> start;for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ ){string a = word[i], b = word[j];for (int k = 1; k < min(a.size(), b.size()); k ++ ) //因為重合的越少dragon越長,所以k從1個開始if (a.substr(a.size() - k, k) == b.substr(0, k)) //后綴等于前綴{g[i][j] = k;break; //找到最小的就好}}for (int i = 0; i < n; i ++ )if (word[i][0] == start)dfs(word[i], i);cout << ans << endl;return 0; }
  • 分成互質組
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 給定 n 個正整數,將它們分組,使得每組中任意兩個數互質。

    至少要分成多少個組?

    輸入格式
    第一行是一個正整數 n。

    第二行是 n 個不大于10000的正整數。

    輸出格式
    一個正整數,即最少需要的組數。

    數據范圍
    1≤n≤10
    輸入樣例:
    6
    14 20 33 117 143 175
    輸出樣例:
    3

    //可以轉化成圖論問題,如果兩1118. 分成互質組//如果是組合問題就不要做成排列問題了 #include <bits/stdc++.h> #define endl '\n' using namespace std; typedef long long ll;int n, len; int ans; int a[10]; vector<int>g[10];int inline gcd(int a, int b) {return b ? gcd(b, a % b) : a; }bool inline check(int v, int u) {for (int i = 0; i < g[v].size(); i ++ )if (gcd(g[v][i], u) > 1) return 0;return 1; }void dfs(int u) {if (u == n){ans = min(ans, len);return ;}for (int i = 0; i < len; i ++ )if (check(i, a[u])){g[i].push_back(a[u]);dfs(u + 1);g[i].pop_back();}g[len ++ ].push_back(a[u]);dfs(u + 1);g[ -- len].pop_back(); }int main() {scanf("%d", &n);for (int i = 0; i < n; i ++ ) scanf("%d", a + i);ans = n;dfs(0);printf("%d\n", ans); } #include <bits/stdc++.h> using namespace std; const int N = 15;int a[N]; int n, ans = N; bool st[N]; int group[N][N];int inline gcd(int a, int b) {return b ? gcd(b, a % b) : a; }bool inline check(int g, int gc, int i) {for (int j = 0; j < gc; j ++ ){if (gcd(a[group[g][j]], a[i]) != 1) return false;}return true; }void dfs(int g, int gc, int tc, int start) {if (g >= ans) return ;if (tc == n){ans = min(ans, g);return ;}bool flag = 1;for (int i = start; i < n; i ++ ){if (!st[i] && check(g, gc, i)){st[i] = 1;group[g][gc] = i;dfs(g, gc + 1, tc + 1, i + 1);st[i] = 0;flag = 0;}}if (flag) dfs(g + 1, 0, tc, 0); }int main() {cin >> n;for (int i = 0; i < n; i ++ ) scanf("%d", a + i);dfs(1, 0, 0, 0);cout << ans << endl;return 0; } //2.2.2 DFS之剪枝 //1.優化搜索順序 //2.排除等效冗余 //3.可行性剪枝 //4.最優性剪枝 //5.記憶化搜索(DP)
  • 小貓爬山
    題目
    提交記錄
    討論
    題解
    視頻講解
  • 翰翰和達達飼養了 N 只小貓,這天,小貓們要去爬山。

    經歷了千辛萬苦,小貓們終于爬上了山頂,但是疲倦的它們再也不想徒步走下山了(嗚咕>_<)。

    翰翰和達達只好花錢讓它們坐索道下山。

    索道上的纜車最大承重量為 W,而 N 只小貓的重量分別是 C1、C2……CN。

    當然,每輛纜車上的小貓的重量之和不能超過 W。

    每租用一輛纜車,翰翰和達達就要付 1 美元,所以他們想知道,最少需要付多少美元才能把這 N 只小貓都運送下山?

    輸入格式
    第 1 行:包含兩個用空格隔開的整數,N 和 W。

    第 2…N+1 行:每行一個整數,其中第 i+1 行的整數表示第 i 只小貓的重量 Ci。

    輸出格式
    輸出一個整數,表示最少需要多少美元,也就是最少需要多少輛纜車。

    數據范圍
    1≤N≤18,
    1≤Ci≤W≤108
    輸入樣例:
    5 1996
    1
    2
    1994
    12
    29
    輸出樣例:
    2
    難度:簡單
    時/空限制:1s / 64MB
    總通過數:5596
    總嘗試數:12283
    來源:《算法競賽進階指南》
    算法標簽

    #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 20;ll n, w, ans = N; vector<ll>g; ll cat[N];void dfs(int u) {if (g.size() >= ans) return ;if (u == n){ans = min(ans, (ll)g.size());return ;}for (int i = 0; i < g.size(); i ++ ){if (g[i] + cat[u] <= w){g[i] += cat[u];dfs(u + 1);g[i] -= cat[u];}}g.push_back(cat[u]);dfs(u + 1);g.pop_back(); }int main() {cin >> n >> w;for (int i = 0; i < n; i ++ ) scanf("%d", cat + i);dfs(0);cout << ans << endl;return 0; }

    #include <bits/stdc++.h> using namespace std; const int N = 20;int n, w; int cat[N]; int ans = N; int sum[N];void dfs(int u, int k) {if (k >= ans) return ; //最優性剪枝if (u == n){ans = k;return ;}for (int i = 0; i < k; i ++ ){if (sum[i] + cat[u] <= w) //可行性剪枝{sum[i] += cat[u];dfs(u + 1, k);sum[i] -= cat[u];}}sum[k] = cat[u]; //[0, k - 1],所以sum[k]是新開的dfs(u + 1, k + 1);sum[k] -= cat[u]; }int main() {cin >> n >> w;for (int i = 0; i < n; i ++ ) scanf("%d", cat + i);//優化搜索順序sort(cat, cat + n);reverse(cat, cat + n);dfs(0, 0);cout << ans << endl;return 0; }

    總結

    以上是生活随笔為你收集整理的AcWing算法提高课 Level-3 第二章 搜索的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。