注意:这类型题最好在for循环里面定义变量 i;因为每次递归新的函数进入循环需要 i 为0;但是如蓝桥杯等,C99之前的C语言编译器for循环里面不能定义变量;但是C++可以,所以尽量使用C++
深度优先搜索基本形式:
void dfs(...)//(int x,int y,int z)
{
if(满足条件的等式)//(x==n&&y==m)
{
if()//(sum>z)
{
//(z=sum)
}
return;
}
for(int i;i<t;i++)//(t为题目的走法,如(上下左右走):
//实现就要定义一个全局变量 int move[4][2]=
//{{1,0},{0,1},{-1,0},{0,-1})
{
int tx=x+move[i][0];
int ty=y+move[i][1];
if(tx和ty满足图的条件和搜索这个图的条件)
//tx>=0&&tx<n&&ty>=0&&ty<m && map[tx][ty]
{
dfs(tx,ty,z的走法);
}
}
}
输出最的长度
例题1:
问题描述
小袁非常喜欢滑雪, 因为滑雪很刺激。为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。 小袁想知道在某个区域中最长的一个滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。如下:
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
你的任务就是找到最长的一条滑坡,并且将滑坡的长度输出。 滑坡的长度定义为经过点的个数,例如滑坡24-17-16-1的长度是4。
输入格式
输入的第一行表示区域的行数R和列数C(1<=R, C<=10)。下面是R行,每行有C个整数,依次是每个点的高度h(0<= h <=10000)。
输出格式
只有一行,为一个整数,即最长区域的长度。
样例输入
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
样例输出
25
#include<stdio.h>
#include<math.h>
int n,m;
int map[100][100];
int move[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int flag[100][100];
int count=0;
int z=0;
void dfs(int x,int y)
{
if(flag[x][y]>1)
{
return;
}
for(int i=0;i<4;i++)
{
int tx=x+move[i][0];
int ty=y+move[i][1];
if(tx>=0&&tx<n&&ty>=0&&ty<m&&map[tx][ty]>map[x][y])
{
dfs(tx,ty);
flag[x][y]=fmax(flag[x][y],flag[tx][ty]+1);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf("%d",&map[i][j]);
flag[i][j]=1;
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
dfs(i,j);
count=fmax(count,flag[i][j]);
}
}
printf("%d",count);
return 0;
}
例题2:
在一行中的一个点,由对应步数前走或者后走到达目的需要多少次移动
输入格式:
2行
第一行是3个正整数 n a b ;
表示书的 总页数 起始页数 目标页数
第二行有n个整数 N1 N2 N3 … Ni…Nn;
分别表示在第i页,可以向前或向后翻动的页数
注意:数据保证 0 <= a,b,Nx <= n ; 0 < n < 300.
输出格式:
一个整数 表示所需的最少的翻页次数 翻不到则输出-1.
输入样例:
5 1 5
3 3 1 2 5
输出样例:
3
#include<stdio.h>
int n,a,b;
int map[1000];
int enen=0;
int flag[10001];
int move[2]={1,-1};
int count=1000000;
void dfs(int x,int y)
{
if(x==b)
{
if(count>y)
{
count=y;
}
enen=1;
return;
}
for(int i=0;i<2;i++)
{
int ex=x+map[x]*move[i];
if(ex>=1&&ex<=n&&flag[ex]==1)
{
flag[x]=0;
dfs(ex,y+1);
flag[x]=1;
}
}
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)
{
scanf("%d",&map[i]);
flag[i]=1;
}
dfs(a,0);
if(enen==1)
{
printf("%d",count);
}
else
{
printf("-1");
}
return 0;
}
第三题
有一个 n*m 的棋盘 (1<= n,m <=101) ,在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
帮助
马在棋盘上运动一步的坐标变化可以用一个二维数组表示,如:
int MoveTo[8][2]= { {1,2},{1,-2},{2,1},{2,-1},{-1,2},{-1,-2},{-2,1},{-2,-1} };
注意输出格式
输入格式:
一行四个数据,棋盘的大小 n,m 和马的坐标 x,y 。
输出格式:
一个n*m的矩阵,代表马到达某个点最少要走几步(左对齐,宽5格,不能到达则输出-1)
行末均无空格。
输入样例:
3 3 1 1
输出样例:
在这里给出相应的输出。例如:
0 3 2
3 -1 1
2 1 4
#include<stdio.h>
int n,m,x1,y1;
int map[101][101];
int flag[101][101];
int move[8][2]={{1,2},{1,-2},{2,1},{2,-1},{-1,2},{-1,-2},{-2,1},{-2,-1}};
void dfs(int x,int y,int z)
{
if(map[x][y]>z)
{
map[x][y]=z;
}
else
{
return;
}
for(int i=0;i<8;i++)
{
int tx=x+move[i][0];
int ty=y+move[i][1];
if(tx>=0&&tx<n&&ty>=0&&ty<m&&flag[tx][ty]==1)
{
flag[x][y]=0;
dfs(tx,ty,z+1);
flag[x][y]=1;
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&x1,&y1);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
map[i][j]=10000;
flag[i][j]=1;
}
}
dfs(x1-1,y1-1,0);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(map[i][j]==10000)
{
map[i][j]=-1;
}
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
printf("%-5d",map[i][j]);
}
if(i!=n-1)
{
printf("\n");
}
}
return 0;
}
试题 算法提高 最大值路径
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
刷微博,编程序。如下图所示,@北京发布 提出了如下“头脑震荡”问题。对此问题做一般化描述:
有n阶方阵,从矩阵的左下角元素为起点,从行或列(水平或垂直)两个方向上移动,直到右上角。求出有多少条路径可以使得经过的元素累加值最大,最大值是多少。
输入格式
共有n+1行。
第一行整数n,表示矩阵的阶数,2<=n<=10。
第二行起,每行n个整数,以空格分隔,共n行。。
输出格式
一行,两个空格分隔的数,第一个表示最大值路径的条数,第二个表示最大值。
样例输入
5
4 5 4 5 6
2 6 5 4 6
2 6 6 5 2
4 5 2 2 5
5 2 5 6 4
样例输出
3 47
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int map[11][11];
int flag[11][11];
int sum=1;
int cnt=-1;
int sun=0;
int move[2][2]={{-1,0},{0,1}};
void dfs(int x,int y,int z)
{
if(x==0&&y==n-1)
{
if(z>cnt)
{
sum=1;
cnt=z;
}
else if(cnt==z)
{
sum++;
}
return;
}
for(int i=0;i<2;i++)
{
int tx=x+move[i][0];
int ty=y+move[i][1];
if(tx>=0&&tx<n&&ty>=0&&ty<n)
{
dfs(tx,ty,z+map[tx][ty]);
}
}
}
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cin >> map[i][j];
}
}
dfs(n-1,0,map[n-1][0]);
cout << sum << " " << cnt;
return 0;
}
试题 历届试题 分考场
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
n个人参加某项特殊考试。
为了公平,要求任何两个认识的人不能分在同一个考场。
求是少需要分几个考场才能满足条件。
输入格式
第一行,一个整数n(1<n<100),表示参加考试的人数。
第二行,一个整数m,表示接下来有m行数据
以下m行每行的格式为:两个整数a,b,用空格分开 (1<=a,b<=n) 表示第a个人与第b个人认识。
输出格式
一行一个整数,表示最少分几个考场。
样例输入
5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
样例输出
4
样例输入
5
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
样例输出
5
#include<iostream>
#include<algorithm>
using namespace std;
int flag[100][100];//flag[1][0]=x;表示第i个考场第零个位置上坐了x
int map[100][100];
int n,m;
int minn;
void dfs(int x,int y)
{
if(y>=minn)
{
return;
}
if(x>n)
{
minn=y;
return;
}
for(int i=1;i<=y;i++)//逐个考场搜索
{
int k=0;
while(flag[i][k]&&!map[x][flag[i][k]])
{
k++;
}
if(!flag[i][k])//如果满足map[a][b]==1不会进行
{
flag[i][k]=x;
dfs(x+1,y);
flag[i][k]=0;
}
}
flag[y+1][0]=x;
dfs(x+1,y+1);
}
int main()
{
cin>>n>>m;
int a,b;
minn=n;
for(int i=0;i<m;i++)
{
cin>>a>>b;
map[a][b]=map[b][a]=1;
}
dfs(1,0);
cout<<minn;
return 0;
}
试题 历届试题 格子刷油漆
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形(如下图所示),现需要把这些格子刷上保护漆。
你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!)
比如:a d b c e f 就是合格的刷漆顺序。
c e f d a b 是另一种合适的方案。
当已知 N 时,求总的方案数。当N较大时,结果会迅速增大,请把结果对 1000000007 (十亿零七) 取模。
输入格式
输入数据为一个正整数(不大于1000)
输出格式
输出数据为一个正整数。
样例输入
2
样例输出
24
样例输入
3
样例输出
96
样例输入
22
样例输出
359635897
这题不能用dfs做,时间复杂度度太大
在这里插入代码片
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int map[1000][1000];
ll cnt=0;
ll enen=0;
ll flag[1000][1000];
ll n;
ll move[8][2]={{1,0},{0,1},{-1,0},{0,-1},{1,-1},{-1,1},{1,1},{-1,-1}};
void dfs(int x,int y,int z)
{
if(z==2*n-1)
{
cnt++;
return;
}
for(int i=0;i<8;i++)
{
int tx=x+move[i][0];
int ty=y+move[i][1];
if(tx>=0&&tx<2&&ty>=0&&ty<n&&!flag[tx][ty])
{
flag[x][y]=1;
dfs(tx,ty,z+1);
flag[x][y]=0;
}
}
}
int main()
{
cin>>n;
if(n==1)
{
printf("2");
return 0;
}
for(int i=0;i<2;i++)
{
for(int j=0;j<n;j++)
{
dfs(i,j,0);
}
}
cout<<cnt;
return 0;
}
输出最的路径
这种方法时间复杂度特别大
7-3 单向TSP(训练赛) (35 分)
给一个m行n列(m≤10,n≤100)的整数矩阵,从第一列任何一个位置出发每次往右、右上或右下走一格,最终到达最后一列。要求经过的整数之和最小。整个矩阵是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解时输出字典序最小的。
输入格式:
第一行输入行数m和列数n
第二行输入m*n的矩阵。
输出格式:
第一行输出最优路线,第二行输出最小整数之和。
输入样例:
5 6
3 4 1 2 8 6
6 1 8 2 7 4
5 9 3 9 9 5
8 4 1 3 2 6
3 7 2 8 6 4
输出样例:
在这里给出相应的输出。例如:
1 2 3 4 4 5
16
#include<stdio.h>
int map[100][10000];
int m,n;
int move[3][2]={{0,1},{1,1},{-1,1}};
int count=10000;
int oo[10000];
int flag[10000];
int enen;
int sum1=0,sum2=0;
void dfs(int x,int y,int nn)
{
if(y==1)
{
flag[1]=x;
}
if(nn>count)
{
return;
}
if(y==m)
{
if(count==nn)
{
enen=1;
for(int i=1;i<=m;i++)
{
if(oo[i]>flag[i])
{
enen=0;
break;
}
else if(oo[i]==flag[i])
{
continue;
}
else if(oo[i]<flag[i])
{
break;
}
}
if(enen==0)
{
for(int i=1;i<=m;i++)
{
oo[i]=flag[i];
}
}
}
else if(count>nn)
{
count=nn;
for(int i=1;i<=m;i++)
{
oo[i]=flag[i];
}
}
return;
}
for(int i=0;i<3;i++)
{
int tx=x+move[i][0];
if(tx<1)
{
tx=n+tx;
}
else if(tx>n)
{
tx=tx-n;
}
int ty=y+move[i][1];
if(tx>=1&&tx<=n&&ty>=1&&ty<=m)
{
flag[ty]=tx;
dfs(tx,ty,nn+map[tx][ty]);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
}
}
for(int i=1;i<=n;i++)
{
dfs(i,1,map[i][1]);
}
for(int i=1;i<=m;i++)
{
printf("%d",oo[i]);
if(i!=m)
{
printf(" ");
}
}
printf("\n");
printf("%d",count);
return 0;
}