作为一个大渣,初学算法和数据结构,刚学完链表,便接触了搜索,深知其重要,潜心修炼数天,终于从DFS找到点
感觉了然BFS还刚开始(流泪流泪),简直是一跪再跪,一晕再晕。近几日,做了一些DFS的基础题,好不容易各方搜索
才搜到的,现分享给大家,并赋予一些自己写的代码,希望各位大神指导交流。
1、桐桐的全排列(可在 http://acm.upc.edu.cn/problem.php?id=2355(中国石油acm网) 提交)
Description
今天,桐桐的老师布置了一道数学作业,要求列出所有从数字1到数字n的连续自然数的排列,要求所产生的任一数字
序列中不允许出现重复的数字。因为排列数很多,桐桐害怕写漏了,所以她决定用计算机编程来解决。
Input
只有一个整数n(1≤n≤9)。
Output
按字典序输出由1~n组成的所有不重复的数字序列,每行一个序列,每个数字之间有一个空格。
Sample Input
3
Sample Output
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
分析:这应该是我认为最基础的DFS题了,不需要太多想法,只需要按照DFS的理念一直往下搜,搜到底部,之后回溯
即可。DFS的一般理念设定一个数组visit[],作为判断当前是否已被遍历过。
代码如下
#include <stdio.h>
#include <string.h>
int a[1000],visit[1000],n;
int cunchu[10];
void dfs(int k )
{
int i;
if (k==n)//递归边界
{
for (i=0;i<k-1;i++) printf("%d ",cunchu[i]);
printf("%d\n",cunchu[k-1]);
return;
}
for (i=0;i<n;i++)
{
if (visit[i]==0)
{
visit[i]=1;
cunchu[k]=a[i];//存储每个位的数字
dfs(k+1);//搜索下一个数字
visit[i]=0;//回溯,搜索下一种情况
}
}
}
int main()
{
int s=0,i;
scanf("%d",&n);
memset(visit,0,sizeof(visit));//数组初始化
for (i=0;i<n;i++) a[i]=i+1;
dfs(0);
return 0;
}:
思考:这个题的数字限定在1~9;
那么(1)、加入0(0不能位于首位)。
(2)、加入重复数字(不准输出相同数字)。一步步加深难度,有兴趣可以做一下。
2、还是一个递归加上回溯的题目。
素数环(杭电hdu-1016)
时间限制:1000 ms | 内存限制:65535 KB
描述
有一个整数n,把从1到n的数字无重复的排列成环,且使每相邻两个数(包括首尾)的和都为素数,称为素数环。
为了简便起见,我们规定每个素数环都从1开始。例如,下图就是6的一个素数环。
输入
有多组测试数据,每组输入一个n(0<n<20),n=0表示输入结束。
输出
如果存在满足题意叙述的素数环,从小到大输出。
否则输出No Answer。
样例输入
6
8
3
0
样例输出
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4
Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2
Case 3:
No Answer
分析:这个题难度便比上一个题难度加大了不少,但是有一个易发现的规律:如果想相邻两个数和为素数,那么素数
环中必定是奇偶相隔,所以如果n是奇数,必定有两个奇数相邻的问题,如此便输出“No Answer”。当n==1时算作自
环,输出1。这个问题便简化了一大半了。
简化2;设立一个素数数组,把下标当数字,之后存储他是否是素数,0为素数,1为不是素数。
int prime[40]={1,1,0,0,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,01,1,1,0,1,1};
简化3:依旧是设立一个visit[]数组,判断遍历与否。
代码如下:
#include <cstdio>
#include <cstring>
int prime[40]={1,1,0,0,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,01,1,1,0,1,1};
int visit[21],ring[21];//ring[]存储环上的数字
void DFS(int k,int n)
{
int i;
if (k==n+1&&!prime[ring[1]+ring[n]])//递归边界
{
printf("1");//1每次都作为开头输出
for (i=2;i<=n;i++)
printf(" %d",ring[i]);
printf("\n");
return;
}
for (i=2;i<=n;i++)
{
if (!visit[i]&&!prime[i+ring[k-1]])//回溯加递归
{
visit[i]=1;
ring[k]=i;
DFS(k+1,n);
visit[i]=0;
}
}
}
int main()
{
int T,n;
T=1;
while(scanf("%d",&n),n)
{
printf("Case %d:\n",T++);
if(n==1)
{
printf("1\n");//特殊情况1
continue;
}
if(n&1)
{
printf("No Answer\n");//奇数
continue;
}
memset(visit,0,sizeof(visit));
visit[1]=ring[1]=1;
DFS(2,n);
}
return 0;
}
3、油田,八连快问题(来源http://poj.org/problem?id=1562)
Description
The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits.
GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides
the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to
determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two
pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and
may contain numerous pockets. Your job is to determine how many different oil deposits are contained
in a grid.
Input
The input contains one or more grids. Each grid begins with a line containing m and n, the number of
rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input;
otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not
counting the end-of-line characters). Each character corresponds to one plot, and is either `*',
representing the absence of oil, or `@', representing an oil pocket.
Output
are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100
pockets.
Sample Input
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
0 0
Sample Output
0
1
2
2
分析:这个题也是DFS里的基础题,亦是设定一个visit[]数组,判断遍历与否。每次访问它便给他一个连通编号,最
后输出连通编号即可。
代码如下:
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1000;
int m,n,idx[maxn][maxn];
char pic[maxn][maxn];
void dfs(int r,int c,int id)
{
if (r<0||r>=m||c<0||c>=n) return;
if (idx[r][c]>0||pic[r][c]!='@') return;
int dr,dc;
idx[r][c]=id;
for (dr=-1;dr<=1;dr++)
for (dc=-1;dc<=1;dc++)
if (dc!=0||dr!=0) dfs(r+dr,c+dc,id);
}
int main()
{
int i,j;
while (scanf("%d%d",&m,&n)==2&&m&&n)
{
int count=0;
for (i=0;i<m;i++) scanf("%s",pic[i]);
memset(idx,0,sizeof(idx));
for (i=0;i<m;i++)
for (j=0;j<n;j++)
if (idx[i][j]==0&&pic[i][j]=='@') dfs(i,j,++count);
printf("%d\n",count);
}
return 0;
}
4、细胞问题(来源http://acm.qust.edu.cn/problem.php?id=1230)
一矩形阵列由数字0到9组成,数字1到9为细胞数字,若沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形
阵列的细胞个数。
例如,
0234500067
1034560500
2045600671
0000000089,有4个细胞(细胞为加粗体区域)
输入
第一行是一个测试数据的个数T,表示将会有T组测试数据。
每组测试数据的第一行:两个数字M N (1<=M<=50 1<=N<80)表示该阵列有M行N列。
从第2行到第M+1行每行有连续的N个字符。
输出
与测试数据对应,刚好有T行,每行输出对应测试数据的细胞个数。
样例输入
1
4 10
0234500067
1034560500
2045600671
0000000089
样例输出
4
分析:此题与上一个题可以说是完全相似,只是每个位置的遍历方向减少了四个,只有上下左右,不多做赘述,直接
上代码。
代码如下:
#include <cstdio>
#include <cstring>
int visit[51][81],m,n;
char a[51][81];
void dfs(int r,int c,int id)
{
if (r<0||c<0||r>=m||c>=n) return;
if (visit[r][c]>0||a[r][c]=='0')return;
visit[r][c]=id;
dfs(r+1,c,id);dfs(r-1,c,id);
dfs(r,c+1,id);dfs(r,c-1,id);
}
int main()
{
int cases,i,j;
scanf("%d",&cases);
getchar();
while (cases--)
{
int ans=0;
scanf("%d%d",&m,&n);
getchar();
memset(visit,0,sizeof(visit));
for (i=0;i<m;i++)
scanf("%s",a[i]);
for (i=0;i<m;i++)
for (j=0;j<n;j++)
{
if (visit[i][j]==0&&a[i][j]!='0') dfs(i,j,++ans);
}
printf("%d\n",ans);
}
return 0;
}
5、拯救OIBH总部(来源于http://acm.qust.edu.cn/problem.php?id=1101)
OIBH被突来的洪水淹没了> .< 还好OIBH总部有在某些重要的地方起一些围墙,用*号表示,而一个封闭的*号区域洪
水是进不去的……现在给出OIBH的围墙建设图,问OIBH总部没被淹到的重要区域(由" 0" 表示)有多少。
输入
第一行是两个数,x和y(x,y< =500) 第二行及以下是一个由*和0组成的x*y的图。
输出
输出没被水淹没的OIBH总部的“0”的数量。
样例输入
5 4
00000
00*00
0*0*0
00*00
样例输出
1
分析:其实与上面的两个题还是类似的,但是此题有一个特别的地方,模仿洪水从四个方向朝里面涌入(和种子填充类似),最后判定那
几个’0‘没被遍历过,便是所求。
代码如下:
#include <cstdio>
#include <cstring>
#define maxn 1000
int a[maxn][maxn],visit[maxn][maxn]={0},m,n;
void DFS(int i,int j)
{
if (a[i][j]==0||visit[i][j]==1)
return;
visit[i][j]=1;
DFS(i-1,j);DFS(i+1,j);
DFS(i,j-1);DFS(i,j+1);
}
int main()
{
int m,n,i,j;
char ch;
scanf("%d%d",&m,&n);
getchar();
for (i=1;i<=m;i++)
{
for (j=1;j<=n;j++)
{
scanf("%c",&ch);
if (ch=='*') a[i][j]=0;
else if (ch=='0')a[i][j]=1;
}
getchar();
}
for (i=1;i<=m;i++)
{
if (a[i][0]==1&&!visit[i][0]==0) DFS(i,0);
if (a[i][n]==1&&!visit[i][n]) DFS(i,n);
}
for (i=2;i<=n-1;i++)
{
if (a[0][i]==1&&!visit[0][i]) DFS(0,i);
if (a[m][i]==1&&!visit[m][i]) DFS(m,i);
}
int ans=0;
for (i=1;i<=m;i++)
for (j=1;j<=n;j++)
if (a[i][j]==1&&visit[i][j]==0) ans++;
printf("%d\n",ans);
return 0;
}
6、另外在最后给大家几个关于DFS的题
POJ1011 POJ1321 POJ1562 POJ1979 POJ 3620
还有石油大学里的搜索专题http://acm.upc.edu.cn/contest.php?cid=1036
NYOJ20 HDU1518 HDU1445 ZOJ1002 HDU1269 HDU2181 POJ3620
滑雪问题(这个需要记忆性,只看过大神的代码,自己不会写,也抄袭一下吧,谢谢那位大神)
代码如下:
#include<stdio.h>
#include<string.h>
int a[109][109],f[109][109];
int dir[4][2]={0,-1,0,1,-1,0,1,0};
int r,c;
int dfs(int x,int y)
{
int i,j,xx,yy,num;
for(i=0;i<4;i++)
{
xx=x+dir[i][0];
yy=y+dir[i][1];
if(xx>=0&&xx<r&&yy>=0&&yy<c&&a[xx][yy]>a[x][y])
{
if(f[xx][yy]>1)
num=f[xx][yy];
else
num=dfs(xx,yy);
if((num+1)>f[x][y])
f[x][y]=num+1;
}
}
return f[x][y];
}
int main()
{
int i,j,max,m;
while(scanf("%d%d",&r,&c)!=EOF)
{
for(i=0;i<r;i++)
for(j=0;j<c;j++)
scanf("%d",&a[i][j]);
for(i=0;i<r;i++)
for(j=0;j<c;j++)
f[i][j]=1;
for(i=0;i<r;i++)
for(j=0;j<c;j++)
{
f[i][j]=dfs(i,j);
}
max=0;
for(i=0;i<r;i++)
for(j=0;j<c;j++)
{
if(f[i][j]>max)
max=f[i][j];
}
printf("%d\n",max);
}
return 0;
}