算法刷题day55:搜索(二)

2024-05-28 11:36
文章标签 算法 搜索 刷题 day55

本文主要是介绍算法刷题day55:搜索(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 引言
  • 一、迷宫
  • 二、红与黑
  • 三、马走日
  • 四、单词接龙
  • 五、分成互质组
  • 六、小猫爬山
  • 七、数独
  • 八、木棒
  • 九、加成序列
  • 十、排书

引言

上篇文章主要是讲 B F S BFS BFS 的,主要应用在搜索中找最短路方面,主要就是在内部搜索,和整体搜索。而 D F S DFS DFS 其实就是暴力,主要介绍如果写出暴力,并且写出优化与剪枝,有时候能用 B F S BFS BFS ,就用它,因为不容易失误,但是用 D F S DFS DFS 就是不会的时候用,好处就是可以剪枝。那就还是以题目的方式,进行讲解。


一、迷宫

标签:DFS

思路:其实这道题用 B F S BFS BFS 就是最保险的,因为就是一个基本的最短路,但因为本章节是讲 D F S DFS DFS 的,所以就拿 D F S DFS DFS 写了,也顺便粘一个 B F S BFS BFS 的吧,没什么说的,详情见代码。

题目描述:

一天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

示例代码1:DFS版

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 110, M = N, INF = 0x3f3f3f3f;int n, m;
char g[N][N];
bool st[N][N];
PII S, E;int dir[4][2] = {0,1,0,-1,1,0,-1,0};bool dfs(int x, int y)
{st[x][y] = true;if(x == E.x && y == E.y) return true;for(int i = 0; i < 4; ++i){int a = x + dir[i][0];int b = y + dir[i][1];if(a < 0 || a >= n || b < 0 || b >= n) continue;if(st[a][b] || g[a][b] == '#') continue;if(dfs(a,b)) return true;}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);int T; cin >> T;while(T--){memset(st, 0, sizeof st);cin >> n;for(int i = 0; i < n; ++i) cin >> g[i];cin >> S.x >> S.y >> E.x >> E.y;bool flag;if(g[S.x][S.y] == '#' || g[E.x][E.y] == '#') flag = false;else flag = dfs(S.x,S.y);if(flag) cout << "YES" << endl;else cout << "NO" << endl;}return 0;
}

示例代码2:BFS

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 110, M = N, INF = 0x3f3f3f3f;int n, m;
char g[N][N];
bool st[N][N];
PII S, E;int dir[4][2] = {0,1,0,-1,1,0,-1,0};bool bfs()
{if(g[S.x][S.y] == '#' || g[E.x][E.y] == '#') return false;  // 没说,起始点可能就是死路memset(st, 0, sizeof st);st[S.x][S.y] = true;queue<PII> q; q.push(S);while(q.size()){auto t = q.front(); q.pop();if(t == E) return true;  // 写这里而不是下面,防止起点即终点for(int i = 0; i < 4; ++i){int x = t.x + dir[i][0];int y = t.y + dir[i][1];if(x < 0 || x >= n || y < 0 || y >= n) continue;if(st[x][y] || g[x][y] == '#') continue;st[x][y] = true;q.push({x,y});}}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);int T; cin >> T;while(T--){cin >> n;for(int i = 0; i < n; ++i) cin >> g[i];cin >> S.x >> S.y >> E.x >> E.y;if(bfs()) cout << "YES" << endl;else cout << "NO" << endl;}return 0;
}

二、红与黑

标签:DFS、Flood Fill

思路:其实就是找从起点开始能走多少个点,也没啥说的,两个版本都在下面,看看即可。

题目描述:

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。输入格式
输入包括多个数据集合。每个数据集合的第一行是两个整数 W 和 H,分别表示 x 方向和 y 方向瓷砖的数量。在接下来的 H 行中,每行包括 W 个字符。每个字符表示一块瓷砖的颜色,规则如下1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。当在一行中读入的是两个零时,表示输入结束。输出格式
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。数据范围
1≤W,H≤20
输入样例:
6 9 
....#. 
.....# 
...... 
...... 
...... 
...... 
...... 
#@...# 
.#..#. 
0 0
输出样例:
45

示例代码1:DFS

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 25, M = N, INF = 0x3f3f3f3f;int n, m;
char g[N][N];
bool st[N][N];
PII S;int dir[4][2] = {0,1,0,-1,1,0,-1,0};int dfs(int x, int y)
{if(g[x][y] == '#') return 0;  // 防止起始点为障碍st[x][y] = true;int res = 1;for(int i = 0; i < 4; ++i){int a = x + dir[i][0];int b = y + dir[i][1];if(a < 0 || a >= n || b < 0 || b >= m) continue;if(st[a][b] || g[a][b] == '#') continue;res += dfs(a,b);}return res;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);while(cin >> m >> n, n || m){memset(st, 0, sizeof st);for(int i = 0; i < n; ++i){cin >> g[i];for(int j = 0; j < m; ++j){if(g[i][j] == '@') S = {i,j};}}cout << dfs(S.x,S.y) << endl;}return 0;
}

示例代码2:BFS

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 25, M = N, INF = 0x3f3f3f3f;int n, m;
char g[N][N];
bool st[N][N];
PII S;int dir[4][2] = {0,1,0,-1,1,0,-1,0};int bfs()
{memset(st, 0, sizeof st);st[S.x][S.y] = true;int res = 1;queue<PII> q; q.push(S);while(q.size()){auto t = q.front(); q.pop();for(int i = 0; i < 4; ++i){int x = t.x + dir[i][0];int y = t.y + dir[i][1];if(x < 0 || x >= n || y < 0 || y >= m) continue;if(st[x][y] || g[x][y] == '#') continue;  // 这里写='#' 是因为起点或者终点是有符号的,而不是!='.' res++;st[x][y] = true;q.push({x,y});}}return res;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);while(cin >> m >> n, n || m) // 看清楚行列顺序 {for(int i = 0; i < n; ++i) {cin >> g[i];for(int j = 0; j < m; ++j){if(g[i][j] == '@') S = {i,j};}}cout << bfs() << endl;}return 0;
}

三、马走日

标签:DFS

思路:整体思想就是每一个点走的八个方向都是一种方案,只要在遍历过程中能把所有点都给走满,就是一种方案。所以可以定义一个全局变量来记录,然后传一个当前走过点的数量,用来判断是否走满了,然后就是正常的写法了,当然也可以让其返回每个方向上的数量,不用全局变量,也是可以的,这里就不写了。

题目描述:

马在中国象棋以日字形规则移动。请编写一段程序,给定 n∗m 大小的棋盘,以及马的初始位置 (x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途
径遍历棋盘上的所有点。输入格式
第一行为整数 T,表示测试数据组数。每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标 n,m,x,y。输出格式
每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,若无法遍历棋盘上的所有点则输出 0。数据范围
1≤T≤9,1≤m,n≤9,1≤n×m≤28,0≤x≤n−1,0≤y≤m−1
输入样例:
1
5 4 0 0
输出样例:
32

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 10, M = N, INF = 0x3f3f3f3f;int n, m;
bool st[N][N];
PII S;
int ans;int dir[8][2] = {-2,-1, -2,1, -1,-2, -1,2, 1,-2, 1,2, 2,-1, 2,1};void dfs(int cur, int x, int y)  // 当前正在访问第cur个合法状态{x,y} 
{if(cur == n * m) {ans++;return;}st[x][y] = true;for(int i = 0; i < 8; ++i)  // 每一种方向代表一种方案 {int a = x + dir[i][0];int b = y + dir[i][1];if(a < 0 || a >= n || b < 0 || b >= m) continue;if(st[a][b]) continue;dfs(cur+1,a,b);}st[x][y] = false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);int T; cin >> T;while(T--){memset(st, 0, sizeof st);ans = 0;cin >> n >> m >> S.x >> S.y;dfs(1,S.x,S.y);cout << ans << endl;}return 0;
}

四、单词接龙

标签:DFS

思路:这道题就是传入一个单词,然后遍历自己的子串长度,再遍历单词组,查看是否满足要求,如果满足就接上去,然后定义一个全局变量找最大龙的长度。另外开头为一个字母,需要特判一下。

题目描述:

单词接龙是一个与我们经常玩的成语接龙相类似的游戏。现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”,每个单词最多被使用两次。在两个单词相连时,其重合部分合为一部分,例如 beast 和 astonish ,如果接成一条龙则变为 beastonish。我们可以任意选择重合部分的长度,但其长度必须大于等于1,且严格小于两个串的长度,例如 at 和 atide 间不能相连。输入格式
输入的第一行为一个单独的整数 n 表示单词数,以下 n 行每行有一个单词(只含有大写或小写字母,长度不超过20),输入的最
后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。输出格式
只需输出以此字母开头的最长的“龙”的长度。数据范围
n≤20,单词随机生成。输入样例:
5
at
touch
cheat
choose
tact
a
输出样例:
23
提示
连成的“龙”为 atoucheatactactouchoose。

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 25, M = N, INF = 0x3f3f3f3f;int n, m;
string str[N], src;
unordered_map<string,int> cnt;
int ans;void dfs(string dragon, bool flag)
{ans = max(ans, (int)dragon.size());for(int len = 1; len <= dragon.size(); ++len){int size1 = dragon.size();if(!flag && len == size1) continue;string t1 = dragon.substr(size1-len);for(int i = 0; i < n; ++i){if(cnt[str[i]] >= 2) continue;int size2 = str[i].size();if(size2 < 1 || len > size2) continue;string t2 = str[i].substr(0,len);if(t1 != t2) continue;cnt[str[i]]++;dfs(dragon+str[i].substr(len),false);cnt[str[i]]--;}}
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n;for(int i = 0; i < n; ++i) cin >> str[i], cnt[str[i]] = 0;cin >> src;dfs(src,true);cout << ans << endl;return 0;
}

五、分成互质组

标签:DFS

思路:首先互质是两个数的最大公约数为 1 1 1 ,互质组就是任意两个数的最大公约数都为 1 1 1 。这道题就是一个简单的暴搜,一个数要么加入到当前的组里去,要么新开一个组存进去。这里说一下写法,第一种就是朴素的 void dfs(int u, int k) ,这里的 u , k u,k u,k 都是从 0 0 0 开始的,当 u = n u = n u=n 时,说明当前所有的数都已经被放到组里去了,说明当前的 k k k 是一种方案。这里的 k k k 就是组数,我们放数的时候是从 0 ∼ k − 1 0\sim k-1 0k1 枚举的,这样也符合一般的习惯,新开的数组编号就为 k k k ,当前的组数就变为了 k + 1 k+1 k+1 。然后迭代加深的参数列表为 void dfs(int u, int k, int depth) 从开始的求最值变为了求是否是可行解,因为本就是从最小的值变过来的,只要可行就是最优解。具体细节见代码。

题目描述:

示例代码1:朴素暴搜

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 15, M = N, INF = 0x3f3f3f3f;int n, m;
int a[N];
vector<int> g[N];
int ans = N;int gcd(int a, int b)
{return b ? gcd(b,a%b) : a;
}bool check(int x, int k)
{for(int i = 0; i < g[k].size(); ++i){if(gcd(x,g[k][i]) > 1) return false;}return true;
}void dfs(int u, int k)
{if(k >= ans) return;if(u == n){ans = k;return;}for(int i = 0; i < k; ++i){if(check(a[u],i)){g[i].push_back(a[u]);dfs(u+1,k);g[i].pop_back();}}g[k].push_back(a[u]);dfs(u+1,k+1);g[k].pop_back();
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n;for(int i = 0; i < n; ++i) cin >> a[i];dfs(0,0);cout << ans << endl;return 0;
}

示例代码2:迭代加深

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 15, M = N, INF = 0x3f3f3f3f;int n, m;
int a[N];
vector<int> g[N];int gcd(int a, int b)
{return b ? gcd(b,a%b) : a;
}bool check(int x, int k)
{for(int i = 0; i < g[k].size(); ++i){if(gcd(x,g[k][i]) > 1) return false;}return true;
}bool dfs(int u, int k, int depth)
{if(k > depth) return false;if(u == n) return true;for(int i = 0; i < k; ++i){if(check(a[u],i)){g[i].push_back(a[u]);if(dfs(u+1,k,depth)) return true;g[i].pop_back();}}g[k].push_back(a[u]);if(dfs(u+1,k+1,depth)) return true;g[k].pop_back();return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n;for(int i = 0; i < n; ++i) cin >> a[i];int depth = 0;while(!dfs(0,0,depth)) depth++;cout << depth << endl;return 0;
}

六、小猫爬山

标签:搜索、深度优先搜索、DFS

思路:本质上跟上一道题是一模一样,就是 c h e c k check check 函数变了而已。另外这里用到了几个常用的剪枝,首先一般这种重量的都会从大到小去遍历,因为越大放的东西越少,分支就少了。其次就是最优性剪枝和可行性剪枝了,对于一些枚举组的,要用到排除等效冗余,其余的就是正常写法了。

题目描述:

翰翰和达达饲养了 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

示例代码1:朴素暴力

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 20, M = N, INF = 0x3f3f3f3f;int n, m;
int w[N];
int sum[N];
int ans = 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((LL)w[u] + sum[i] <= m)  // 可行性剪枝{sum[i] += w[u];dfs(u+1,k);sum[i] -= w[u];}}sum[k] += w[u];dfs(u+1,k+1);sum[k] -= w[u];
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 0; i < n; ++i) cin >> w[i];sort(w,w+n,greater<int>());  // 优化搜索顺序 dfs(0,0);cout << ans << endl;return 0;
}

示例代码2:迭代加深

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 20, M = N, INF = 0x3f3f3f3f;int n, m;
int w[N];
int sum[N];bool dfs(int u, int k, int depth)
{//这里要判断是否可行,而不是最优 if(k > depth) return false;  // 最优性剪枝if(u == n) return true; for(int i = 0; i < k; ++i){if((LL)w[u] + sum[i] <= m)  // 可行性剪枝{sum[i] += w[u];if(dfs(u+1,k,depth)) return true;sum[i] -= w[u];}}sum[k] += w[u];if(dfs(u+1,k+1,depth)) return true;sum[k] -= w[u];return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 0; i < n; ++i) cin >> w[i];sort(w,w+n,greater<int>());  // 优化搜索顺序 int depth = 0;while(!dfs(0,0,depth)) depth++;cout << depth << endl;return 0;
}

七、数独

标签:搜索、深度优先搜索、DFS

思路:这道题整体上是每次找到一个可以填入数字最少的一个(优化搜索顺序),然后填入继续迭代,直至所有的数都被填满了。这里运用了二进制的思想,把每一列看作一个二进制位,然后用二进制位的思想进行优化,具体细节见代码。

题目描述:

数独 是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得数独中每行、每列、每个 3×3 的九宫格内数字 1∼9 均恰
好出现一次。请编写一个程序填写数独。输入格式
输入包含多组测试用例。每个测试用例占一行,包含 81 个字符,代表数独的 81 个格内数据(顺序总体由上到下,同行由左到右)。每个字符都是一个数字(1−9)或一个 .(表示尚未填充)。您可以假设输入中的每个谜题都只有一个解决方案。文件结尾处为包含单词 end 的单行,表示输入结束。输出格式
每个测试用例,输出一行数据,代表填充完全后的数独。输入样例:
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
输出样例:
417369825632158947958724316825437169791586432346912758289643571573291684164875293
416837529982465371735129468571298643293746185864351297647913852359682714128574936

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 9, M = 1 << N, INF = 0x3f3f3f3f;int n, m;
int row[N], col[N], cell[3][3];
int ones[M], mmap[M];
string str;
int cnt;void init()
{cnt = 0;for(int i = 0; i < N; ++i) row[i] = col[i] = (1 << N) - 1;for(int i = 0; i < 3; ++i){for(int j = 0; j < 3; ++j){cell[i][j] = (1 << N) - 1;}}
}void draw(int x, int y, int t, bool is_set)
{if(is_set) str[x*N+y] = t + '1';else str[x*N+y] = '.';int v = 1 << t;if(!is_set) v = -v;row[x] -= v;col[y] -= v;cell[x/3][y/3] -= v;
}int get(int x, int y)
{return row[x] & col[y] & cell[x/3][y/3];
}int lowbit(int x)
{return x & -x;
}bool dfs(int cnt)
{if(!cnt) return true;int minv = 10, x, y;for(int i = 0; i < N; ++i){for(int j = 0; j < N; ++j){if(str[i*N+j] == '.'){int state = get(i,j);if(ones[state] < minv){minv = ones[state];x = i, y = j;}}}}int state = get(x,y);for(int i = state; i; i -= lowbit(i)){int t = mmap[lowbit(i)];draw(x,y,t,true);if(dfs(cnt-1)) return true;draw(x,y,t,false);}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);for(int i = 0; i < N; ++i) mmap[1 << i] = i;for(int i = 0; i < 1 << N; ++i){for(int j = 0; j < N; ++j){ones[i] += i >> j & 1;	}} while(cin >> str, str != "end"){init();int cnt = 0;for(int i = 0; i < N; ++i){for(int j = 0; j < N; ++j){if(str[i*N+j] != '.'){int t = str[i*N+j] - '1';draw(i,j,t,true);}else cnt++;}}if(dfs(cnt)) cout << str << endl;}return 0;
}

八、木棒

标签:搜索、DFS、剪枝

思路:这道题是枚举组,因为只有当一组满了,才能去枚举下一组,如果当前组都不能填满,那就是错的。然后这个剪枝的策略也是比较的经典,可以看看。

题目描述:

乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50 个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。输入格式
输入包含多组数据,每组数据包括两行。第一行是一个不超过 64 的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。数据范围
数据保证每一节木棍的长度均不大于 50。输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 70, M = N, INF = 0x3f3f3f3f;int n, m;
int w[N];
int len, sum;
bool st[N];bool dfs(int u, int s, int start)
{if(u * len == sum) return true;  // 说明前u组已经满了 if(s == len) return dfs(u+1,0,0);for(int i = start; i < n; ++i)  // 排除性冗余 {if(!st[i] && (LL)w[i] + s <= len){st[i] = true;if(dfs(u,s+w[i],i+1)) return true;st[i] = false;if(!s || s + w[i] == len) return false; int j = i + 1;while(j < n && w[i] == w[j]) j++;i = j - 1;}}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);while(cin >> n, n){memset(st, 0, sizeof st);len = sum = 0;for(int i = 0; i < n; ++i) {cin >> w[i];sum += w[i];len = max(len, w[i]);}sort(w, w+n, greater<int>());  // 优化搜索顺序 while(true){if(sum % len == 0 && dfs(0,0,0)){cout << len << endl;break;}len++;}}return 0;
}

九、加成序列

标签:搜索、迭代加深

思路:枚举每一个数,每一个数可能的情况,然后进行判断,最后的结果是否可行,然后利用迭代加深的性质找出最小值。一些搜索剪枝已经在代码中给出,更多细节见代码。

题目描述:

满足如下条件的序列 X(序列中元素被标号为 1、2、3…m)被称为“加成序列”:X[1]=1
X[m]=n
X[1]<X[2]<…<X[m−1]<X[m]
对于每个 k(2≤k≤m)都存在两个整数 i 和 j (1≤i,j≤k−1,i 和 j 可相等),使得 X[k]=X[i]+X[j]。
你的任务是:给定一个整数 n,找出符合上述条件的长度 m 最小的“加成序列”。如果有多个满足要求的答案,只需要找出任意一个可行解。输入格式
输入包含多组测试用例。每组测试用例占据一行,包含一个整数 n。当输入为单行的 0 时,表示输入结束。输出格式
对于每个测试用例,输出一个满足需求的整数序列,数字之间用空格隔开。每个输出占一行。数据范围
1≤n≤100
输入样例:
5
7
12
15
77
0
输出样例:
1 2 4 5
1 2 4 6 7
1 2 4 8 12
1 2 4 5 10 15
1 2 4 8 9 17 34 68 77

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 110, M = N, INF = 0x3f3f3f3f;int n, m;
int path[N];bool dfs(int u, int depth)  // 正在选 下标为u的数 
{if(u > depth) return false;if(u == depth) return path[u-1] == n;bool st[N] = {0};for(int i = u - 1; i >= 0; --i){for(int j = i; j >= 0; --j)  // 排除行冗余 {int s = path[i] + path[j];if(st[s] || s <= path[u-1] || s > n) continue;  // 排除性冗余st[s] = true;path[u] = s; if(dfs(u+1,depth)) return true;}}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);while(cin >> n, n){path[0] = 1;int depth = 1;while(!dfs(1,depth)) depth++;for(int i = 0; i < depth; ++i){cout << path[i] << " ";}cout << endl;}return 0;
}

十、排书

标签:搜索、IDA*

思路:首先估价函数就是没排书一次会改变三个状态的正确性,然后最少需要 ⌈ r e s 3 ⌉ \lceil \frac{res}{3}\rceil 3res ,这个估价函数的设置必须是小于等于真实值的。然后进行迭代加深,根据其性质第一次可行即最优解,然后就是每次操作,进行暴力枚举所有可能的情况。

题目描述:

给定 n 本书,编号为 1∼n。在初始状态下,书是任意排列的。在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。我们的目标状态是把书按照 1∼n 的顺序依次排列。求最少需要多少次操作。输入格式
第一行包含整数 T,表示共有 T 组测试数据。每组数据包含两行,第一行为整数 n,表示书的数量。第二行为 n 个整数,表示 1∼n 的一种任意排列。同行数之间用空格隔开。输出格式
每组数据输出一个最少操作次数。如果最少操作次数大于或等于 5 次,则输出 5 or more。每个结果占一行。数据范围
1≤n≤15
输入样例:
3
6
1 3 4 6 2 5
5
5 4 3 2 1
10
6 8 5 3 4 7 2 9 1 10
输出样例:
2
3
5 or more

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 20, M = N, INF = 0x3f3f3f3f;int n, m;
int w[N];int f()
{int res = 0;for(int i = 1; i < n; ++i){if(w[i] != w[i-1] + 1) res++;}return (res + 2) / 3;
}bool dfs(int u, int depth)
{if(u + f() > depth) return false;  // 已经完成u次操作if(!f()) return true; for(int len = 1; len <= n; ++len){for(int l = 0; l + len - 1 < n; ++l){int r = l + len - 1;for(int k = r + 1; k < n; ++k)  // 移动到k的后面{//把[r+1,k]移动到[l,r], 把[l,r]移动到之后int backup[N]; memcpy(backup, w, sizeof w);int i = l, j = r + 1;for(; j <= k; ++i, ++j) w[i] = backup[j];for(j = l; j <= r; ++i, ++j) w[i] = backup[j];if(dfs(u+1,depth)) return true;memcpy(w, backup, sizeof w);}}}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);int T; cin >> T;while(T--){cin >> n;for(int i = 0; i < n; ++i) cin >> w[i];int depth = 0;while(depth < 5 && !dfs(0,depth)) depth++;if(depth >= 5) cout << "5 or more" << endl;else cout << depth << endl;}return 0;
}

这篇关于算法刷题day55:搜索(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1010450

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

hdu1240、hdu1253(三维搜索题)

1、从后往前输入,(x,y,z); 2、从下往上输入,(y , z, x); 3、从左往右输入,(z,x,y); hdu1240代码如下: #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#inc

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int