3.8深度优先搜索dfs
3.8深度优先搜索dfs
洛谷题目传送门
- P1451 求细胞数量
- P1596 [USACO10OCT]Lake Counting S
- UVA572 油田 Oil Deposits
- P5461 赦免战俘
- P1706 全排列问题
- P1157 组合的输出
- P1605 迷宫
- P1219 [USACO1.5]八皇后 Checker Challenge
P1451 求细胞数量
【题目描述】
一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,
细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,
求给定矩形阵列的细胞个数。
【输入格式】
第 1 行两个整数代表矩阵大小 n 和 m。
接下来 n 行,每行一个长度为 m 的只含字符 0 到 9 的字符串,代表这个 n×m 的矩阵。
【输出格式】
一行一个整数代表细胞个数。
输入样例:
4 10
0234500067
1034560500
2045600671
0000000089
输出样例: 4
数据规模与约定:对于 100% 的数据,保证 1≤n,m≤100。
【参考题解】
#include<bits/stdc++.h>
using namespace std;
const int N=110;
char a[N][N];
int dx[8]={-1, 0, 1, 0};
int dy[8]={ 0,-1, 0, 1};
struct T{
int x,y;
};
int n,m,ans=0;
bool in(int x, int y){
if(x>=0&&x<n&&y>=0&&y<m) return 1;
return 0;
}
void print(){
for(int i=0; i<n; i++) printf("%s\n",a[i]);
printf("\n");
}
void dfs(int x,int y){
a[x][y]='0';
for(int i=0; i<4; i++){
int tx=x+dx[i];
int ty=y+dy[i];
if(in(tx,ty) && a[tx][ty]!='0'){
dfs(tx,ty);
}
}
}
int main(){
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i=0; i<n; i++) scanf("%s", a[i]);
// print();
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(a[i][j]!='0'){
dfs(i,j); ans++;
// print();
}
}
}
printf("%d\n", ans);
return 0;
}
P1596 [USACO10OCT]Lake Counting S
【题目描述】
由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。
我们用一个 NxM(1<=N<=100;1<=M<=100) 网格图表示。
每个网格中有水('W') 或是旱地('.')。
一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。
约翰想弄清楚他的田地已经形成了多少水坑。
给出约翰田地的示意图,确定当中有多少水坑。
【输入格式】
第 1 行, 两个空格隔开的整数:N 和 M ,
第 2 行到第 N+1 行, 每行 M 个字符,每个字符是 'W' 或 '.',它们表示网格图中的一排。
字符之间没有空格。
【输出格式】
一行,水坑的数量
输入样例:
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>
using namespace std;
const int N=110;
char a[N][N];
int n,m,ans=0;
int dx[8]={-1,-1,-1, 0, 0, 1, 1, 1};
int dy[8]={-1, 0, 1,-1, 1,-1, 0, 1};
bool in(int x, int y){
if(x>=0&&x<n&&y>=0&&y<m) return 1;
return 0;
}
void dfs(int x, int y){
a[x][y]='.';
for(int i=0; i<8; i++){
int tx=x+dx[i];
int ty=y+dy[i];
if(in(tx,ty) && a[tx][ty]=='W'){
dfs(tx,ty);
}
}
}
void print(){
for(int i=0; i<n; i++){
printf("%s\n",a[i]);
}printf("\n");
}
int main(){
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
scanf("%d%d", &n, &m);
for(int i=0; i<n; i++){
scanf("%s",a[i]);
}
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(a[i][j]=='W') {
dfs(i,j); ans++;
// print(); //中间打印查看问题,要养成好习惯
}
}
}
printf("%d\n", ans);
return 0;
}
UVA572 油田 Oil Deposits
【题目描述】
输入多个 m 行 n 列的矩阵,用 0 0 表示输入结束。
找出有多少块石油区域,用 “@” 代表石油,假如两个 “@” 在横,竖或对角线上相邻,
就说它们位于同一区域,对于每个输入,输出一个数表示有几个石油区域。
输入样例:
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
0 0
输出样例:
0
1
2
2
【参考题解】
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
string s[N];
int dx[8]={-1,-1,-1, 0, 0, 1, 1, 1};
int dy[8]={-1, 0, 1,-1, 1,-1, 0, 1};
bool in(int x, int y){
if(x>=0&&x<n &&y>=0&&y<m) return 1;
return 0;
}
void dfs(int x, int y){
s[x][y]='*';
for(int i=0; i<8; i++){
int tx=x+dx[i];
int ty=y+dy[i];
if(in(tx,ty) && s[tx][ty]=='@'){
dfs(tx,ty);
}
}
}
int main(){
// freopen("data.in", "r", stdin);
while(scanf("%d%d", &n, &m)==2){
if(n==0&&m==0) return 0;
for(int i=0; i<n; i++) cin>>s[i];
int ans=0; //多组数据,需要每次都初始化为 0
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(s[i][j]=='@'){
dfs(i,j); ans++;
}
}
}
printf("%d\n", ans);
}
return 0;
}
P5461 赦免战俘
【题目描述】kkksc03 决定赦免一些作弊者。
现有 2^n × 2^n (n≤10) 名作弊者站成一个正方形方阵等候 kkksc03 的发落。
他将正方形矩阵均分为 4 个更小的正方形矩阵,每个更小的矩阵的边长是原矩阵的一半。
其中左上角那一个矩阵的所有作弊者都将得到赦免,剩下 3 个小矩阵中,
每一个矩阵继续分为 4 个更小的矩阵,然后通过同样的方式赦免作弊者...
直到矩阵无法再分下去为止。
所有没有被赦免的作弊者都将被处以棕名处罚。
给出 n,请输出每名作弊者的命运,其中 0 代表被赦免,1 代表不被赦免。
【输入格式】
一个整数 n。
【输出格式】
2^n × 2^n 的 01 矩阵,代表每个人是否被赦免。数字之间有一个空格。
输入样例: 3
输出样例:
0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 1
0 0 0 0 1 1 1 1
0 0 0 1 0 0 0 1
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1
1 1 1 1 1 1 1 1
【参考题解】
#include<cstdio>
#define N 1030
int a[N][N];
void print(int x1, int y1, int x2, int y2){
// printf("(%2d, %2d) -> (%2d, %2d)\n",x1,y1,x2,y2);
for(int i=x1; i<=x2; i++){
for(int j=y1; j<y2; j++){
printf("%d ", a[i][j] ? 0:1);
}
printf("%d\n", a[i][y2] ? 0:1);
}
}
void dfs(int x1, int y1, int x2, int y2){
if(x1==x2) return;
for(int i=x1; i<=(x1+x2)/2; i++){
for(int j=y1; j<=(y1+y2)/2; j++){
a[i][j]=1;
}
}
// print(x1, y1, x2, y2);
dfs(x1, (y1+y2)/2+1, (x1+x2)/2, y2);
dfs((x1+x2)/2+1, y1, x2, (y1+y2)/2);
dfs((x1+x2)/2+1, (y1+y2)/2+1, x2, y2);
}
int main(){
int n; scanf("%d", &n);
n=(1<<n);
dfs(1,1,n,n);
print(1,1,n,n);
return 0;
}
P1706 全排列问题
【题目描述】
按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,
要求所产生的任一数字序列中不允许出现重复的数字。
【输入格式】
一个整数 n。
【输出格式】
由 1~n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留 5 个场宽。
输入样例: 3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
说明/提示:1≤n≤9。
【参考题解】
#include<bits/stdc++.h>
using namespace std;
int n, a[10],vis[10];
void print(){
for(int i=1; i<=n; i++){
printf("%5d",a[i]);//cout<<setw(5)<<a[i];
}printf("\n");
}
//安置第 m 个的元素
void dfs(int m){
if(m>n){// Why not? m>=n, Because: true == (a[m]==0);
print(); return ;//返回上一级,可以节省少许时间
}
for(int i=1; i<=n; i++){
if(!vis[i]){
a[m] = i; vis[i] = 1;
dfs(m+1); // 继续安置第 m+1 列的元素
vis[i] = 0; //回溯,当前元素取消使用标记
}
}
}
int main(){
scanf("%d", &n);
dfs(1);
return 0;
}
P1157 组合的输出
【题目描述】
排列与组合是常用的数学方法,其中组合就是从 n 个元素中抽出 r 个元素(不分顺序且 r≤n),
我们可以简单地将 n 个元素理解为自然数 1,2,…,n,从中任取 r 个数。
现要求你输出所有组合。
【输入格式】
一行两个自然数 n,r (1<n<21, 0≤r≤n)。
【输出格式】
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,
每个元素占三个字符的位置,所有的组合也按字典顺序。
输入样例: 5 3
输出样例:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
【参考题解】
#include<iostream>
#include<iomanip>
using namespace std;
const int N=21;
int n, r, a[N], vis[N];
void dfs(int m){
if(m>r){
for(int i=1; i<=r; i++){
cout<<setw(3)<<a[i];
}cout<<endl;
return;
}
for(int i=1; i<=n; i++){
if(!vis[i] && i>a[m-1]){
a[m] = i; vis[i] = 1;
dfs(m+1);
vis[i] = 0; //回溯
}
}
return;
}
int main(){
cin>>n>>r;
dfs(1);
return 0;
}
P1605 迷宫
【题目描述】
给定一个 N*M 方格的迷宫,迷宫里有 T 处障碍,障碍处不可通过。
给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。
在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
【输入格式】
第一行N、M和T,N为行,M为列,T为障碍总数。
第二行起点坐标 SX,SY,终点坐标 FX,FY。接下来 T 行,每行为障碍点的坐标。
【输出格式】
给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。
输入样例:
2 2 1
1 1 2 2
1 2
输出样例: 1
说明/提示: 1≤N,M≤5
【参考题解】
#include<iostream>
using namespace std;
const int N=10;
int a[N][N],vis[N][N];
int n, m, t, x1, x2, y1, y2, ans=0;
int dx[4]={-1, 0, 1, 0};
int dy[4]={ 0, 1, 0,-1};
void dfs(int x, int y){
if(x<1 || y<1 || x>n || y>m ||a[x][y]==1) return;
if(x==x2 && y==y2){
ans++; return;
}
a[x][y]=1;//一个点只能走一次
for(int i=0; i<4; i++){
dfs(x+dx[i], y+dy[i]);
}
a[x][y]=0;
}
int main(){
cin>>n>>m>>t;
cin>>x1>>y1>>x2>>y2;
for(int i=1; i<=t; i++) {
int x,y; cin>>x>>y;
a[x][y]=1;
}
dfs(x1,y1);
cout<<ans;
return 0;
}
EZOI 瓷砖
【题目描述】
在一个 w×h 的矩形广场上,每一块 1×1 的地面都铺设了红色或黑色的瓷砖。
小林同学站在某一块黑色的瓷砖上,他可以从此处出发,
移动到上、下、左、右四个相邻的且是黑色的瓷砖上。
现在,他想知道,通过重复上述移动所能经过的黑色瓷砖数。
【输入格式】
第 1 行为 h、w,2≤w、h≤50,之间由一个空格隔开;
以下为一个 w 行 h 列的二维字符矩阵,每个字符为“.”“#”“@”,
分别表示该位置为黑色的瓷砖、红色的瓷砖、小林的初始位置。
【输出格式】
输出一行一个整数,表示小林从初始位置出发经过的黑色瓷砖数。
输入样例:
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
输出样例:59
【参考题解】
#include<iostream>
using namespace std;
const int N=55;
int h,w,ans=0;
char a[N][N];
int dis[][2]={{-1, 0},{ 0,-1},{ 1, 0},{ 0, 1}};
void print(){
for(int i=0; i<h; i++) printf("%s\n", a[i]);
printf("\n");
}
bool in(int x, int y){
if(x>=0 && x<w && y>=0 && y<h) return 1;
return 0;
}
void dfs(int x, int y){
a[x][y]='#'; ans++; //进入搜索,计数器 +1
for(int i=0; i<4; i++){
int tx=x+dis[i][0];
int ty=y+dis[i][1];
if(in(tx,ty) && a[tx][ty]=='.'){
dfs(tx,ty); //满足搜索条件
}
}
}
int main(){
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
scanf("%d%d",&h,&w); // w 行 h 列
for(int i=0; i<w; i++) scanf("%s", a[i]);
for(int i=0; i<w; i++){
for(int j=0; j<h; j++){
if(a[i][j]=='@'){
dfs(i,j); break;//只有一个起点,搜索后直接退出
}
}
}
printf("%d\n", ans);
return 0;
}
马的遍历2
【题目描述】
中国象棋的规则是马走“日”,现在有一个 8*8 的棋盘,某个格子里放有一个马。
现在指定一个目标点, 你的任务是判定在给定步数内能否移动这个马到指定的目标点。
注意:马不能走到棋盘外的点,因此靠近边界的马并不能到达所有的点,棋盘上的点横纵坐标均从 1 计数。
【输入格式】
第一行两个坐标 x,y,c,(x,y)表示马的起始位置,c 表示最多走几步
第二行两个坐标 a,b,表示马将要到达的位置
【输出格式】
输出一行 yes 或 no 表示可以或不可以实现
输入样例1:
1 1 1
2 3
输出样例1:yes
输入样例2:
1 1 3
8 8
输出样例2:no
测试点数据范围:
对于50%的数据: 1 <= c <= 5;
对于100%的数据: 1 <= c <= 25;
【参考题解】
#include<iostream>
#include<queue>
using namespace std;
const int N=30;
int vis[N][N];
int dis[][2]={
{-2, -1},{-1,-2},{ 1,-2},{ 2,-1},
{ 2, 1},{ 1, 2},{-1, 2},{-2, 1}};
bool in(int x,int y){
if(x>=1 && x<=8 && y>=1 && y<=8) return 1;
return 0;
}
void print(){
for(int i=1; i<=8; i++){
for(int j=1; j<=8; j++){
printf("%d", vis[i][j]);
}printf("\n");
}printf("\n");
}
void dfs(int x, int y, int step){
vis[x][y]=step+1;
// print(); //输出调试
if(step<=0) return; // 0步特殊处理
for(int i=0; i<8; i++){
int tx=x+dis[i][0];
int ty=y+dis[i][1];// 如果当前步数过小,就被更新
if(in(tx,ty) && step>0 && vis[tx][ty]<step+1){
dfs(tx, ty, step-1);
}
}
}
int main(){
freopen("horse.in", "r", stdin);
freopen("horse.out", "w", stdout);
int x,y,c,a,b;
cin>>x>>y>>c>>a>>b;
dfs(x,y,c);
if(vis[a][b]) cout<<"yes";
else cout<<"no";
return 0;
}