深度优先搜索C语言例题

注意:这类型题最好在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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值