DFS,就是暴力搜索,去枚举所有的方案,每个DFS都会对应于一个搜索树,我们再考虑DFS问题的时候,首先要考虑的就是顺序,用一个怎么样的顺序能得到所有的方案

文章目录

  • 快速入门
    • 排列数字
    • N 皇后
  • 连通块模型
    • 迷宫
    • 红与黑
  • 搜索顺序
    • 马走日
    • 单词接龙
    • 分成互质组
  • 剪枝
    • 常用技巧
    • 小猫爬山
    • 数独
    • 木棍
  • 迭代加深
    • 加成序列
  • 双向DFS
    • 送礼物
  • IDA*
    • 排书
    • 回转游戏
  • leetcode
    • 605.岛屿的最大面积

快速入门

排列数字

题目描述
给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。现在,请你按照字典序将所有的排列方法输出。

输入格式
共一行,包含一个整数 n。

输出格式
按字典序输出所有排列方案,每个方案占一行。

数据范围

1 <= n <= 7

题解

之前说过, 每个DFS都会对应一个搜索树, 我们要想一个顺序能够枚举到所有的方案, 从我们所画的这个树来看, 实际上就是暴力枚举每个位置上能放的数字。 每到一层我都会确定一个位置上的数, 当所有位置都被放上数字的时候, 就意味着这条分支已经末尾了,这个时候我就要开始准备回溯, 也就是从1 2 3的状态恢复到1 2 空, 可以理解为你进来的时候是什么样子, 你出去的时候就是什么样子。

这里举一个实际的例子,当周末你爸妈出去,留你一个人在家,走之前家里是干干净净的,如果说回来家里乱乱糟糟的,结果可想而知。为了不要挨打,你要在爸妈回来之前,把屋子收拾好,他们走之前是什么样子,回来就是什么样子的,这个过程实际上就是回溯的过程,下面让我们来看一看用代码如何实现。

代码实现

#include<iostream>
using namespace std;
const int N  = 10;
bool  st[N];//全排列中是不允许数字重复的,所以用一个bool数组来记录每个数是否被用过
int path[N];//用来记录结果
int n;
void dfs(int u){//如果说我们到达最后一层,说明我们找到了一组方案if(u == n){for(int i = 0;i<n;i++){cout<<path[i]<<" ";            }cout<<endl;}//向该位置上添加数字for(int i = 1;i<=n;i++){if(!st[i]){//如果说这个数在这条路径上没有用过,说明这个位置上可以用这个树来进行填充st[i] = true;path[u] = i;dfs(u+1);//恢复现场,我进入的时候是怎么样的,出去的时候就要是怎么样path[u] = 0;st[i] = false;}}
}
int main(){cin>>n;dfs(0);return 0 ;
}

N 皇后

题目描述
n− 皇后问题是指将 n 个皇后放在n × n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式
共一行,包含整数 n。

输出格式
每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。

注意:行末不能有多余空格。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BahXtRJZ-1641720141168)(https://g.yuque.com/gr/latex?1%E2%89%A4n%E2%89%A49#card=math&code=1%E2%89%A4n%E2%89%A49&id=51fa6594)]

思路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E9V7bAGR-1641720141169)(https://cdn.jsdelivr.net/gh/freedom0123/img/N%E7%9A%87%E5%90%8E.png#id=JpXI9&originHeight=981&originWidth=1892&originalType=binary&ratio=1&status=done&style=none)]
这个DFS所对应的树就是这样的, 从行开始枚举, 如左图, 如果说这个皇后放在这个位置上, 然后考虑下一行, 看一下, 在上一行的基础基础之上, 当前这一行中那些位置能够放皇后。 这里就会涉及到一个问题, 如何判断当前位置能不能放皇后呢, 根据题意,列,行, 正对角线, 反对角线上只能有一个皇后, 但是因为我们是按行开始枚举的, 所以说行中就不能有重复的皇后, 直接pass。 现在只剩下了列, 正对角线, 反对角线, 通过数学推导, 我们可以很容易得到两个对角线的方程, 通过方程我们知道, 这一条线上的所有点的横纵坐标加起来都对应与一个值, 我们可以通过这个映射值来判断当前这条线上是否有元素。 但是由于i-u 有可能会越界, 所以加一个偏移量, 可以这样理解, 这条线上所有点的横纵坐标都是相同的, 当他们同时加上一个n, 他们仍然是相同的。

代码描述

#include<iostream>
#include<cstring>
using namespace std;
const int N = 10;
char path[N][N];
bool col[N],dg[N*2],udg[N*2];
int n;
void dfs(int u){if(u == n){for(int i = 0 ;i < n; i++){cout<<path[i]<<endl;}cout<<endl;return;}for(int i = 0 ; i < n; i++){if(!dg[i+u] && !udg[i-u+n] && !col[i]){col[i] = dg[i+u] = udg[i-u+n] = true;path[u][i] = 'Q';dfs(u+1);col[i] = dg[i+u] = udg[i-u+n] = false;path[u][i] = '.';}}
}
int main(){cin>>n;for(int i = 0 ;i < n; i++){for(int j = 0;j < n; j++){path[i][j] = '.';}}dfs(0);return 0;
}

连通块模型

迷宫

题目描述
一天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

思路
![BEED9F43-C632-4E18-BEC8-3DFE5D6CE611.jpeg](https://img-blog.csdnimg.cn/img_convert/8e9843b78d402f4b9609de70c5e2c1e0.png#clientId=u546c988a-3854-4&from=ui&id=ucd16d9d5&margin=[object Object]&name=BEED9F43-C632-4E18-BEC8-3DFE5D6CE611.jpeg&originHeight=626&originWidth=1537&originalType=binary&ratio=1&size=313429&status=done&style=none&taskId=u8a82f494-50c0-4812-9f14-9b975ca8f60)

代码描述

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N  = 110;
char path[N][N];
bool st[N][N];
int k;
int n;
int xa,ya,xb,yb;
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,-1,1};
bool dfs(int x,int y){if (path[x][y] == '#') return false;if (x == xb && y == yb) return true;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 >= n) continue;if (st[a][b]) continue;if (dfs(a, b)) return true;}return false;
}
int main(){cin>>k;while(k--){cin>>n;for(int i = 0;i<n;i++){scanf("%s",path[i]);}scanf("%d%d%d%d",&xa,&ya,&xb,&yb);memset(st, 0, sizeof st);if(dfs(xa,ya)){cout<<"YES"<<endl;}else{cout<<"NO"<<endl;}}return 0;
}

红与黑

题目描述

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。

你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。

请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

输入格式

输入包括多个数据集合。

每个数据集合的第一行是两个整数 W 和 H,分别表示 x 方向和 y 方向瓷砖的数量。

在接下来的 H 行中,每行包括 W 个字符。每个字符表示一块瓷砖的颜色,规则如下

1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。

当在一行中读入的是两个零时,表示输入结束。

输出格式

对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。

思路详解

每个dfs都会对应一棵树,而树都会有一个根节点,相当于这个棋盘中的起始位置,而这个节点都会有四个分支,相当于上下左右方向所到达的点,所以和上一题的树是一样的。本质上就是求这个连通块的大小

代码

#include<cstring>
#include<iostream>
using namespace std;
const int N  = 30;
bool st[N][N];
char path[N][N];
int n ,m;
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,1,-1};
int dfs(int x,int y){int cnt  =1;st[x][y] = true;for(int i = 0;i<4;i++){int a = x+dx[i];int b = y+dy[i];if(a<0 || a>=n || b<0 ||b>=m)  continue;if(st[a][b]) continue;if(path[a][b]!='.') continue;cnt+=dfs(a,b);}return cnt;
}
int main(){while(cin>>m>>n , n||m){for(int i = 0;i<n;i++){cin>>path[i];}int x ,y;for(int i = 0;i<n;i++){for(int j = 0;j<m;j++){if(path[i][j]=='@'){x = i;y = j;}}}memset(st,0,sizeof st);cout<<dfs(x,y)<<endl;}return 0;
}

搜索顺序

马走日

题目描述
马在中国象棋以日字形规则移动。
请编写一段程序,给定n∗m大小的棋盘,以及马的初始位置 (x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。

输入格式
第一行为整数 T,表示测试数据组数。

每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标 n,m,x,y。

输出格式
每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,若无法遍历棋盘上的所有点则输出 0。

数据范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pKb7ndLE-1641720141172)(https://g.yuque.com/gr/latex?1%E2%89%A4T%E2%89%A49%2C%0A%0A1%E2%89%A4m%2Cn%E2%89%A49%2C%0A%0A0%E2%89%A4x%E2%89%A4n%E2%88%921%2C%0A%0A0%E2%89%A4y%E2%89%A4m%E2%88%921#card=math&code=1%E2%89%A4T%E2%89%A49%2C%0A%0A1%E2%89%A4m%2Cn%E2%89%A49%2C%0A%0A0%E2%89%A4x%E2%89%A4n%E2%88%921%2C%0A%0A0%E2%89%A4y%E2%89%A4m%E2%88%921&id=4eb1106c)]

输入样例

1
5 4 0 0

输出样例

32

#include<iostream>
#include<cstring>
using namespace std;
const int N = 10;
int n,m,x,y,ans;
bool st[N][N];int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
int 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] = true;for(int i = 0;i < 8;i++){int a = dx[i]+x;int b = dy[i]+y;if(a < 0 || a>=n || b < 0 || b>=m) continue;if(st[a][b]) continue;dfs(a,b,cnt+1);    }st[x][y] = false;
}
int main(){int t;cin>>t;while(t--){memset(st,0,sizeof st);ans  = 0;cin>>n>>m>>x>>y;dfs(x,y,1);cout<<ans<<endl;}return 0;
}

单词接龙

分成互质组

题意描述
给定 n 个正整数,将它们分组,使得每组中任意两个数互质。

至少要分成多少个组?

输入格式

第一行是一个正整数 n。

第二行是 n 个不大于10000的正整数。

输出格式

一个正整数,即最少需要的组数。

数据范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xdEFdAm5-1641720141173)(https://g.yuque.com/gr/latex?1%E2%89%A4n%E2%89%A410#card=math&code=1%E2%89%A4n%E2%89%A410&id=126e38ae)]

输入样例:
6
14 20 33 117 143 175

输出样例:
3
思路

代码说明

#include <iostream>
#include <vector>using namespace std;const int N = 10;int n;
int a[N]; // 存数字
int ans=N, len; // ans:全局答案 len:当前开的组数
vector<int> g[N]; // 每一组int gcd(int x,int y)
{return y ? gcd(y, x % y) : x;
}bool check(int u,int c) // u:当前组,c:组编号,判断当前数能否放到这一组中
{for(int i=0;i<g[c].size();i++)if(gcd(g[c][i], u) > 1) return false;return true;
}void dfs(int u)
{if(u==n){ // 处理完所有数字了ans = min(ans,len);return ;}// 不开新组for(int i = 0;i < len;i++){if(check(a[u], i)){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()
{cin>>n;for(int i=0;i<n;i++) cin>>a[i];dfs(0); // 从下标为0的数字开始搜cout<<ans<<endl;return 0;
}

剪枝

常用技巧

  1. 优先搜索分支较少的分支
  2. 不重复搜素:不会出现重复的结果
  3. 可行性剪枝:如果说当前方案不可行,就没有必要向下走
  4. 最优性剪枝:如果说当前分支已经比不上最优解了,就没有必要向下走了

小猫爬山

题目描述

翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。

经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。

翰翰和达达只好花钱让它们坐索道下山。

索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、C2……CN。

当然,每辆缆车上的小猫的重量之和不能超过 W。

每租用一辆缆车,翰翰和达达就要付 1 美元,所以他们想知道,最少需要付多少美元才能把这 N 只小猫都运送下山?

输入格式

第 1 行:包含两个用空格隔开的整数,N 和 W。

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

输出格式

输出一个整数,表示最少需要多少美元,也就是最少需要多少辆缆车。

数据范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQ12pN2M-1641720141174)(https://g.yuque.com/gr/latex?1%E2%89%A4N%E2%89%A418%2C%0A1%E2%89%A4Ci%E2%89%A4W%E2%89%A4108#card=math&code=1%E2%89%A4N%E2%89%A418%2C%0A1%E2%89%A4Ci%E2%89%A4W%E2%89%A4108&id=b72cc05b)]

代码

import java.io.*;
import java.util.*;
import java.util.Collections;public class Main {static int N  = 20;static int n,m;static int[] w = new int[N];static int[] s = new int[N];static String[] res = new String[2*N];static int ans = N;public static void reverse(){int i = 0;int j = n-1;while(i <= j){int t = w[i];w[i] = w[j];w[j] = t;i++;j--;}}public static void dfs(int u,int k){if(k >= ans){return;}if(u == n){ans = k;return;}for(int i = 0;i < k;i++){if(s[i] + w[u] <= m){s[i] += w[u];dfs(u+1,k);s[i] -= w[u];}}s[k] = w[u];dfs(u+1,k+1);s[k] = 0;}public static void main(String[] args) throws IOException {BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));res = cin.readLine().split(" ");n = Integer.parseInt(res[0]);m = Integer.parseInt(res[1]);for(int i = 0; i < n ;i++){w[i] = Integer.parseInt(cin.readLine());}Arrays.sort(w,0,n);reverse();dfs(0,0);System.out.println(ans);cin.close();}
}

数独

题意描述
数独是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得图中每行、每列、每个 3×3 的九宫格内数字 1∼9 均恰好出现一次。

请编写一个程序填写数独。

输入格式
输入包含多组测试用例。

每个测试用例占一行,包含 81 个字符,代表数独的 81 个格内数据(顺序总体由上到下,同行由左到右)。

每个字符都是一个数字(1−9)或一个 .(表示尚未填充)。

您可以假设输入中的每个谜题都只有一个解决方案。

文件结尾处为包含单词 end 的单行,表示输入结束。

输出格式
每个测试用例,输出一行数据,代表填充完全后的数独。

思路
对于DFS来说, 最重要的是搜索的顺序, 要能够枚举到所有的方案, 这个题对应的一个DFS搜索顺序就是这样的,

  1. 每次任意选可以填的位置
  2. 枚举这个位置的所有可选方案
  3. 递归下一层

确定完搜索顺序之后, 就该考虑了, 在搜索的时候, 这个位置上能放哪个元素,根据数组本身的特点,这个位置上能够放的元素一定是这一行,这一列,这一个九宫格子中没有 出现过的。如果说集合来表示这种关系的话, 实际上就是这三个集合的一个交集。

这里我们使用一个二进制数来表示这一行中哪个元素没有被用过,如果这个二进制数的第 i 位是 1 ,就表示当前这一行 / 列 / 九宫格上,i 没有被用过,有 n 个 1 就表示这个位置上有几种选择( 这个操作可以使用 lowbit很容易算出来)。

下面我们来举一个例子 0 1 0 1 0 0 1 0 1 就表示当前 这行 / 列 / 九宫格 上,**1 3 6 8** 可以使用, 这里要注意的是, 二进制位是0 ~ 8 ,我们要映射为 1 ~ 9, 求交集就是将这三个数进行 & 运算即可

代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 9;
int row[N],col[N],cell[3][3];
int one[1<<N];//每个数的二进制表示中有多少个1
int map[1<<N];//快速求出 2 的幂
char str[100];
inline int lowbit(int x){return x&-x;
}
void init(){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;}       }
}
inline int get(int x,int y){return row[x] & col[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*9 + j] == '.'){int t = one[get(i,j)];if(t < minv){x = i;y = j;minv = t;}}}} for(int i = get(x,y);i != 0; 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 * 9 +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 * 9 +y] = '.'; }  return false;
}
int main(){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; j -= lowbit(j)) {s++;}one[i] = s;//i 的二进制表示中一共有多少个1 } 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;
}

木棍

题意描述

乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50 个长度单位。

然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。

请你设计一个程序,帮助乔治计算木棒的可能最小长度。

每一节木棍的长度都用大于零的整数表示。

输入格式

输入包含多组数据,每组数据包括两行。

第一行是一个不超过 64 的整数,表示砍断之后共有多少节木棍。

第二行是截断以后,所得到的各节木棍的长度。

在最后一组数据之后,是一个零。

输出格式

为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

数据范围

数据保证每一节木棍的长度均不大于 50。

输入样例:

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

输出样例

6
5

思路详解

代码实现

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N  = 70;
int n,length,sum;
int w[N];
bool st[N];
bool dfs(int u,int curlen,int start){if(u * length == sum) return true;if(curlen == length) return dfs(u+1,0,0);for(int i = start;i < n;i++){if(st[i]) continue;if(curlen + w[i] > length) continue;st[i] = true;if (dfs(u, curlen + w[i], i + 1)) return true;st[i] = false;if (!curlen || curlen + w[i] == length) return false;int j = i;while(j < n && w[j] == w[i]) j++;i = j-1;}return false;
}
int main(){while(cin>>n,n){memset(st,0,sizeof st);sum = 0;for(int i = 0;i < n; i++){cin>>w[i];sum+=w[i];}sort(w,w+n);reverse(w,w+n);length = 1;for(;length<=sum;length++){if(sum % length == 0 && dfs(0,0,0)){cout<<length<<endl;break;}}}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<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 110;
int n,depth;
int path[N];
bool st[N];
bool dfs(int u,int k){if(u == k) return path[u-1] == n;memset(st,0,sizeof st);for(int i = u-1;i >= 0; i--){for(int j = i;j>=0;j--){int tem = path[i] + path[j];if(tem > n || tem <= path[u-1] || st[tem]) continue;st[tem] = true;path[u] = tem;if(dfs(u+1,k)) return true;}}return false;}
int main(){while(cin>>n,n){path[0] = 1;depth = 1;while(!dfs(1,depth)) depth++;for(int i = 0 ;i < depth; i++) cout<<path[i]<<' ';cout<<endl;}return  0;
}

双向DFS

送礼物

题目描述

达达帮翰翰给女生送礼物,翰翰一共准备了 N 个礼物,其中第 i 个礼物的重量是 G[i]。

达达的力气很大,他一次可以搬动重量之和不超过 W 的任意多个物品。

达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。

输入格式

第一行两个整数,分别代表 W 和 N。

以后 N 行,每行一个正整数表示 G[i]。

输出格式

仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。

数据范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HhFjMweZ-1641720141175)(https://g.yuque.com/gr/latex?1%E2%89%A4N%E2%89%A446%2C%0A1%E2%89%A4W%2CG%5Bi%5D%E2%89%A4%202%20%5E31%5E-1#card=math&code=1%E2%89%A4N%E2%89%A446%2C%0A1%E2%89%A4W%2CG%5Bi%5D%E2%89%A4%202%20%5E31%5E-1&id=d0c0c33f)]

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N  = 1<<25;
int max_w,n,k;
int cnt = 1;
int ans;//答案
int g[50];
int weight[N];
void dfs1(int u ,int sum){if(u == k) {weight[cnt++] = sum;return;}dfs1(u+1,sum);if((long long)sum + g[u] <= max_w){dfs1(u+1,sum+g[u]);}}
void dfs2(int u ,int sum){if(u == n){int l = 0, r = cnt-1;while(l < r){int mid  = l + r + 1 >>1;if((long long)weight[mid] +sum <= max_w){l = mid;}else{r = mid -1;}}ans = max(ans,weight[l] + sum);return;}dfs2(u+1,sum);if((long long)sum+g[u]<=max_w){dfs2(u+1,sum+g[u]);}}
int main(){cin>>max_w>>n;for(int i = 0 ;i < n; i++) cin>>g[i];sort(g,g+n);reverse(g,g+n);k = n /2 +2;//将前k个物品的重量打一个表dfs1(0,0);sort(weight,weight+cnt);cnt = unique(weight,weight+cnt) - weight;dfs2(k,0);cout<<ans<<endl;return 0;
}

IDA*

排书

题目描述

给定 n 本书,编号为 1∼n。

在初始状态下,书是任意排列的。

在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。

我们的目标状态是把书按照 1∼n 的顺序依次排列。

求最少需要多少次操作。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据包含两行,第一行为整数 n,表示书的数量。

第二行为 n 个整数,表示 1∼n 的一种任意排列。

同行数之间用空格隔开。

输出格式

每组数据输出一个最少操作次数。

如果最少操作次数大于或等于 5 次,则输出 5 or more。

每个结果占一行。

数据范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyS2m4E4-1641720141176)(https://g.yuque.com/gr/latex?1%E2%89%A4n%E2%89%A415#card=math&code=1%E2%89%A4n%E2%89%A415&id=a7a58ac0)]

思路详解

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N  = 15;
int T,n;
int q[N];
int w[5][N];
int f(){int total = 0;for(int i = 0 ;i < n-1; i ++){if(q[i] + 1 != q[i+1]) total++;}return (total + 2) / 3;
}
bool check(){for(int i = 0 ;i < n; i++){if(q[i]!=i+1){return false;}}return true;
}
bool dfs(int u ,int max_depth){if(u + f() > max_depth) return false;if(check()) 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 ++ ){memcpy(w[u], q, sizeof q);int x, y;for (x = r + 1, y = l; x <= k; x ++, y ++ ) q[y] = w[u][x];for (x = l; x <= r; x ++, y ++ ) q[y] = w[u][x];if (dfs(u+ 1, max_depth)) return true;memcpy(q, w[u], sizeof q);}}}return false;
}
int main(){cin>>T;while(T--){cin>>n;for(int i = 0 ;i < n; i++) cin>>q[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;
}

回转游戏

题意描述

如下图所示,有一个 # 形的棋盘,上面有 1,2,3 三种数字各 8 个。

给定 8 种操作,分别为图中的 A∼H。

这些操作会按照图中字母和箭头所指明的方向,把一条长为 7 的序列循环移动 1 个单位。

例如下图最左边的 # 形棋盘执行操作 A 后,会变为下图中间的 # 形棋盘,再执行操作 C 后会变成下图最右边的 # 形棋盘。

给定一个初始状态,请使用最少的操作次数,使 # 形棋盘最中间的 8 个格子里的数字相同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6hmPMG7k-1641720141177)(https://cdn.jsdelivr.net/gh/freedom0123/img/Acwing181.jpg#id=So3KE&originHeight=200&originWidth=528&originalType=binary&ratio=1&status=done&style=none)]

输入格式

输入包含多组测试用例。

每个测试用例占一行,包含 24 个数字,表示将初始棋盘中的每一个位置的数字,按整体从上到下,同行从左到右的顺序依次列出。

输入样例中的第一个测试用例,对应上图最左边棋盘的初始状态。

当输入只包含一个 0 的行时,表示输入终止。

输出格式

每个测试用例输出占两行。

第一行包含所有移动步骤,每步移动用大写字母 A∼H 中的一个表示,字母之间没有空格,如果不需要移动则输出 No moves needed。

第二行包含一个整数,表示移动完成后,中间 8 个格子里的数字。

如果有多种方案,则输出字典序最小的解决方案。

输入样例:

1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0

输出样例:

AC
2
DDHH
2

思路

代码

#include<iostream>
#include<cstring>
using namespace std;
const int N  = 25;
int path[100];
int q[N];
int option[8][8] = {{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 opposite[8] = {5,4,7,6,1,0,3,2};//每个操作的逆过程
int center[8] = {6, 7, 8, 11, 12, 15, 16, 17};//中间的8个格子
int f(){int sum[4];memset(sum,0,sizeof sum);for(int i = 0 ;i < 8;i++) sum[q[center[i]]]++;int s = 0;for(int i = 0 ;i < 4; i++) s = max(s,sum[i]);return 8 - s;
}
bool check(){int st = q[center[0]];for(int i = 0 ;i < 8;i++) {if(q[center[i]] != st){return false;}}return true;
}
void action(int x)
{int t = q[option[x][0]];for (int i = 0; i < 6; i ++ ) q[option[x][i]] = q[option[x][i + 1]];q[option[x][6]] = t;
}bool dfs(int u,int max_depth,int last){if(u + f() > max_depth) return false;if(check()) return true;for(int i = 0 ;i < 8 ; i++){if(opposite[i] == last) continue;action(i);path[u] = i;if(dfs(u+1,max_depth,i)) return true;action(opposite[i]);}return false;}
int main(){while(cin>>q[0] && q[0]){for(int i = 1; i < 24 ; i++) cin>>q[i];int depth = 0;while(!dfs(0,depth,-1)) depth++;if(!depth) cout<<"No moves needed"<<endl;else{for(int i = 0; i < depth; i++){printf("%c",path[i]+'A');}cout<<endl;}cout<<q[6]<<endl;}return 0;
}

leetcode

605.岛屿的最大面积

题目描述

给你一个大小为 m x n 的二进制矩阵 grid 。

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

思路

这题就是DFS的连通块模型,本质上就是寻找最大的连通块的大小

代码描述

class Solution {int N  = 55;int n,m;int dx[] = new int[] {0,-1,0,1};int dy[] = new int[] {-1,0,1,0};boolean[][] st  = new boolean[N][N];public int maxAreaOfIsland(int[][] grid) {int ans = 0;n = grid.length;m = grid[0].length;for(int i = 0;i < n;i++){for(int j  = 0;j < m;j++){if(grid[i][j]!=0){ans = Math.max(ans,dfs(i,j,grid));}}}return ans;}public int  dfs(int x,int y,int[][]grid){int res  = 1;st[x][y] = true;for(int  i = 0;i < 4;i++){int a = x + dx[i];int b = y + dy[i];if(a < 0 || a>=n || b<0 || b>=m || grid[a][b]==0) continue;if(st[a][b]) continue;res += dfs(a,b,grid);}return res;}
}

算法笔记【二】DFS相关推荐

  1. 漫画算法笔记 二叉堆基本操作

    漫画算法笔记 二叉堆基本操作 #include <iostream> #include <stdlib.h> #include <vector> using nam ...

  2. 数据结构与算法笔记(二) 线性表(数组描述)

    c++常用的数据描述方法是数组描述和链式描述,线性表可以用来说明这两方法,先介绍数组描述的线性表.后面再介绍链式描述的线性表. C++ STL容器vector和list相当于线性表的数组描述和链式描述 ...

  3. 基于聚类的推荐算法笔记——以豆瓣电影为例(二)(附源代码)

    基于聚类的推荐算法笔记--以豆瓣电影为例(二)(附源代码) 第一章 聚类算法介绍 基于聚类的推荐算法笔记一 第二章 数据介绍 基于聚类的推荐算法笔记二 第三章 实现推荐算法 基于聚类的推荐算法笔记三 ...

  4. 基于聚类的推荐算法笔记——以豆瓣电影为例(三)(附源代码)

    基于聚类的推荐算法笔记--以豆瓣电影为例(三)(附源代码) 第一章 聚类算法介绍 基于聚类的推荐算法笔记一 第二章 数据介绍 基于聚类的推荐算法笔记二 第三章 实现推荐算法 基于聚类的推荐算法笔记三 ...

  5. 数据结构与算法笔记(十六)—— 二叉搜索树

    一.二叉搜索树定义 二叉搜索树(Binary Search Tree),又名二叉排序树(Binary Sort Tree). 二叉搜索树是具有有以下性质的二叉树: 若左子树不为空,则左子树上所有节点的 ...

  6. 深度学习笔记(10) 优化算法(二)

    深度学习笔记(10) 优化算法(二) 1. Adam 优化算法 2. 学习率衰减 3. 局部最优的问题 1. Adam 优化算法 Adam代表的是 Adaptive Moment Estimation ...

  7. 《Essential C++》笔记之设计一个泛型算法(二)

    前文:<Essential C++>笔记之设计一个泛型算法(一) 相关博文:C++头文件<functional>和bind.placeholders占位符使用简单例子 相关博文 ...

  8. 强化学习经典算法笔记(十二):近端策略优化算法(PPO)实现,基于A2C(下)

    强化学习经典算法笔记(十二):近端策略优化算法(PPO)实现,基于A2C 本篇实现一个基于A2C框架的PPO算法,应用于连续动作空间任务. import torch import torch.nn a ...

  9. 算法笔记-螺旋输出二维数组

    算法笔记-螺旋输出二维数组 1.思路:二维数组看做一个坐标,遍历者当成一个人,那么我们定义这个人的位置,以及当前面朝的方向,还有这个人转向次数.初始位置,人在(x,y)=(0,0)处,面向右方,右方的 ...

  10. 算法笔记(胡凡)学习笔记@Kaysen

    本文旨在记录算法笔记学习过程中的收获和一些知识点,部分易错知识点只针对个人而言,CCF-CSP考试冲鸭!!! Chapter 2 C/C++快速入门(易错知识点) 2.1 基本数据类型 变量定义注意区 ...

最新文章

  1. 路径搜索算法 python实现_A*算法在栅格地图上的路径搜索(python实现)
  2. Git 基本工作流程
  3. python3 isinstance用法_对python中assert、isinstance的用法详解
  4. 【Python】正负无穷
  5. SqlCommandBuilder
  6. 网速提高学习周——系统篇
  7. Sentinel(八)之熔断降级
  8. 电商系统的商品流水记录
  9. 关于vue中使用iconfont
  10. odoo 的字段。orm对象
  11. Android 系统(224)---如何不显示开机SIM卡欢迎语
  12. linux 文件系统 xfs、ext4、ext3 的区别
  13. 【c++ primer】第八章 函数探幽
  14. HTML中常见的其它标签
  15. linux 64位 虚拟内存空间,Linux在x86-64下的虚拟内存布局
  16. ArcGis 10.2运行提示“未授权”的解决方法
  17. android开发之UI
  18. 卷积神经网络(CNN)超详细介绍
  19. Python课程入门之Pycharm创建PY文件的使用步骤与模板创建
  20. Could not open the editor: URLDecoder: Illegal hex characters in escape (%) pattern - For input stri

热门文章

  1. 果 iPhone 4 手机拆机组图,看看 iPhone 4 的内部构造与零件(一)_打杂的_新浪博客...
  2. 微服务网关鉴权——gateway使用、网关限流使用、用户密码加密、JWT鉴权
  3. (已更新)闪照功能娱乐微信小程序源码下载
  4. jQuery中事件的学习
  5. 计算机定时关机教程,msoffice系统工具教程怎么设置电脑定时关机
  6. 基金从业考试如何备考1108
  7. 草根创业者适合做社区团购?社区团购小程序怎么做?
  8. 基础动画-(1)-补间动画
  9. python实战-03-币安量化机器人API接入(更新中)
  10. 树莓派 2 和 3 上的 Swift 3.0