Contest100000608 - 《算法笔记》8.1小节——搜索专题->深度优先搜索(DFS)

▼魔方 西西 提交于 2020-01-22 13:26:30

Contest100000608 - 《算法笔记》8.1小节——搜索专题->深度优先搜索(DFS)

8.1 DFS处理

DFS的概念

在这里插入图片描述

DFS与递归的关系

在这里插入图片描述

递归实现DFS

在这里插入图片描述

DFS递归例一:背包选物

在这里插入图片描述

//DFS递归例子:背包选物 
/*测试数据: 
5 8
3 5 1 2 2
4 5 2 1 3
10
*/
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 30;
int n,V,maxValue = 0;//物品件数、背包容量、最大价值
int w[maxn],c[maxn];//物品重量数组、物品价值数组
//DFS,index为当前处理的物品编号
//sunW和sumC分别为当前总重量和当前总价值
void DFS(int index,int sumW,int sumC){
	if(index == n){//已经完成对n件物品的选择,死胡同
		if(sumW <= V && sumC > maxValue){
			maxValue = sumC;//不超过背包容量时更新最大价值maxValue 
		} 
		return;
	}
	//岔路口
	DFS(index + 1,sumW,sumC);//不选第index件物品
	DFS(index + 1,sumW + w[index],sumC + c[index]);//选第index件物品 
} 
//DFS改进算法,应用背包容量为V筛去不必要的遍历 
void DFS1(int index,int sumW,int sumC){
	if(index == n){//已经完成对n件物品的选择,死胡同
		return;
	}
	//岔路口
	DFS(index + 1,sumW,sumC);//不选第index件物品
	if(sumW + w[index] <= V){
		if(sum + w[index] > ans){
			ans = sumC + c[index];//更新最大价值 
		}
		DFS(index + 1,sumW + w[index],sumC + c[index]);//选第index件物品 
	} 
}
int main(){
	scanf("%d%d",&n,&V);
	for(int i=0;i<n;i++){
		cin>>w[i];
	} 
	for(int i=0;i<n;i++){
		cin>>c[i];
	} 
	DFS1(0,0,0);
	cout<<maxValue<<endl;
	return 0;
}

剪枝改进
在这里插入图片描述

例二:最优子序列

在这里插入图片描述

//例二:最优子序列
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 10010;
//序列A中n个数选k个数使得和为x,最大平方和为maxSumSqu
int n,k,x,maxSumSqu = -1,A[maxn]; 
//temp存放临时方案,ans存放平方和最大的方案
vector<int> temp,ans;
//当前处理index号整数,当前已选整数平方和为sumSqu
void DFS(int index,int nowK,int sum,int sumSqu){
	if(nowK == k && sum == x){//找到k个数的和为x
		if(sumSqu > maxSumSqu){//如果比当前找到的更优
			maxSumSqu = sumSqu;//更新最大平方和
			ans = temp;//更新最优方案 
		}
		return; 
	}
	//已经处理完n个数,或者超过k个数,或者和超过x,返回
	if(index == n || nowK > k || sum > x)	return;
	//选index号数
	temp.push_back(A[index]);
	DFS(index+1,nowK+1,sum+A[index],sumSqu+A[index]*A[index]);
	temp.pop_back();
	//不选index号数
	DFS(index+1,nowK,sum,sumSqu); 
} 
int main(){
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++){
		cin>>A[i];
	} 
	/*
	4 2
	2 3 3 4
	6
	*/
	cin>>x;
	DFS(0,0,0,0);
	cout<<maxSumSqu<<endl;
	return 0;
}

在这里插入图片描述

Codeup习题

Contest100000608 - 《算法笔记》8.1小节——搜索专题->深度优先搜索(DFS)
链接:http://codeup.cn/contest.php?cid=100000608
提交代码链接:http://codeup.cn/status.php?user_id=2015216979&cid=100000608

5972-ProblemA-【递归入门】全排列

链接: http://codeup.cn/problem.php?cid=100000608&pid=0

//5972-ProblemA-【递归入门】全排列
//理论部分有些晕乎 
#include <iostream>
#include <cstdio>
using namespace std; 
const int maxn=15;
const double eps=1e-6;
int n;
int A[maxn];
bool flag[maxn] = {false};
void DFS(int num){//当前处理数组A中下标为num的数 
	//递归边界:下标为n
	if(num == n){
		for(int i=0;i<n-1;i++){
			cout<<A[i]<<" ";
		}
		cout<<A[n-1]<<endl;
		return;
	}
	//递归核心:从低到高选中尚未选中的数 
	for(int i = 0;i < n;i++){
		if(!flag[i]){//未被访问
			A[num] = i+1;//数和下标差1 
			flag[i] = true;//本轮元素访问标记 
			DFS(num + 1);
			flag[i] = false;//为下一轮递归准备 
		}
	} 
}
int main() {
	while(cin>>n){
		DFS(0);
	}
	return 0;
}

5973-ProblemB-【递归入门】组合的输出

链接: http://codeup.cn/problem.php?cid=100000608&pid=1

//5973-ProblemB-【递归入门】组合的输出
// 
#include <iostream>
#include <cstdio>
using namespace std; 
const int maxn=25;
const double eps=1e-6;
int n,r;
int A[maxn];
bool flag[maxn] = {false};
void DFS(int index,int num){//当前处理数组A中下标为index的数,已选num个数 
	//递归边界:下标为n
	int i;
	if(num == r){
		for(int i=0;i<r-1;i++){
			cout<<A[i]<<" ";
		}
		cout<<A[r-1]<<endl;
		return;
	}
	if (index > n-1)//递归边界:已经处理到最后一个节点则返回上一层
		return;
	//递归核心 
	A[num] = index+1;//若选择下标为index的index+1 
	DFS(index + 1,num + 1);//继续选择下一个(直到结束)
						//已尝试过index,返回状态
	DFS(index + 1,num);//若不选择下标为index的index+1	
}
int main() {
	while(cin>>n>>r){
		DFS(0,0);
	}
	return 0;
}

5974-ProblemC-【递归入门】组合+判断素数

链接:http://codeup.cn/problem.php?cid=100000608&pid=2

//5974-ProblemC-【递归入门】组合+判断素数
#include <iostream>
#include <cstdio>
#include <algorithm> 
using namespace std; 
const int maxn=25;
const double eps=1e-6;
int n,k,cnt=0;
int b[maxn];
bool flag[maxn] = {false};
//判断素数
bool isPrime(long long sum){
	for(int i = 2;i <= sqrt(sum); i++){
		if(sum % i == 0){
			return false;
		}
	}
	return true;
} 
//递归选k个数 
void DFS(int index,int num,long long sum){//当前处理数组A中下标为index的数,已选num个数 
	//递归边界:已选k个元素 
	int i;
	if(num == k){
		if(isPrime(sum)){
			cnt++;
			return;
		}
	}
	if (index > n-1)//递归边界:已经处理到最后一个节点则返回上一层
		return;
	//递归核心 
	
	DFS(index + 1,num + 1,sum+b[index]);//若选择下标为index的数b[index],继续选择下一个(直到结束)
	DFS(index + 1,num,sum);//若不选择下标为index的index+1	
}
int main() {
	while(cin>>n>>k){
		cnt = 0;//计数初始化 
		for (int i = 0; i < n; i++) {
			cin >> b[i];
		}
		DFS(0,0,0);
		cout<<cnt<<endl;
	}
	return 0;
}	

5976-ProblemD-【递归入门】n皇后问题(原始的8皇后问题)

链接:http://codeup.cn/problem.php?cid=100000608&pid=3

//5976-ProblemD-【递归入门】n皇后问题(原始的8皇后问题)
//参考前面讲解里面的八皇后回溯法 
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const  int maxn=105;
int N,cnt=0,P[maxn];
char result[105][15]={0};
bool hashTable[maxn] = {false};
bool no_res;
void generateP(int index)
{
	//递归边界 
	if(index == N+1)//若是排列的n位已经处理完,则递归结束
	{
		cnt++;
		for(int i=1;i<N;i++){
			cout<<P[i]<<" ";
		}
		cout<<P[N]<<endl;
		no_res = false;
		return;
	}
	//递归表达式
	for(int x=1;x<=N;x++)//枚举1~n,试图将x填入P[index] 
	{
		if(hashTable[x] == false)//如果x不在P[0]~P[index-1]中,第x行还没有皇后 
		{
			bool flag=true;//flag为true表示当前皇后不会和之前的皇后冲突 
			for(int pre=1;pre<index;pre++)//遍历之前的皇后
			{//第index列皇后的行号为x,第pre列皇后的行号为P[pre]
				if(abs(index-pre) == abs(x-P[pre]))//此处的判别条件?? 
				{
					flag = false;//与之前的皇后在一条对角线,冲突 
					break;
				}
			} 
			
			if(flag)//如果可以把皇后放在第x行
			{
				P[index] = x;//令P的第index位为x,即把x加入当前排列 
				hashTable[x] = true;//记录x已在P中 
				generateP(index+1);	//递归表达式,递归处理下一位 
				hashTable[x] = false;//当递归返回时已处理完P[index]的子问题,还原状态 
			} 
		}	
	}	 
}

int main()
{
	no_res = 1;
	int m;
	scanf("%d",&m);
	N=m;
	generateP(1);
	if(no_res)	cout<<"no solute!"<<endl;
	return 0;
}
	

5977-ProblemE-【递归入门】出栈序列统计

链接:http://codeup.cn/problem.php?cid=100000608&pid=4

//5977-ProblemE-【递归入门】出栈序列统计
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int cnt;//计数 
int n;
void simulate(int push_num,int pop_num){//进栈、出栈数 
	//递归边界
	if(pop_num > push_num || push_num > n ||pop_num > n)	return;
	if(pop_num == push_num && push_num==n){
		cnt++;
		return;
	} 
	//递归表达式
	simulate(push_num+1,pop_num);
	simulate(push_num,pop_num+1);
}

int main(){
	while(cin>>n){
		cnt = 0;
		simulate(0,0);
		cout<<cnt<<endl;
	}
}

	

5978-ProblemF-【递归入门】走迷宫

链接:http://codeup.cn/problem.php?cid=100000608&pid=5

大佬博客:https://blog.csdn.net/qq_36502291/article/details/84678224
//5978-ProblemF-【递归入门】走迷宫
//大佬博客:https://blog.csdn.net/qq_36502291/article/details/84678224
#include <iostream>
#include <cstdio>
using namespace std;
int a[20][20],endx,endy,m,n;
bool no_route = true;//是否存在路线的标志 
int dx[4] = {0,-1,0,1},dy[4]={-1,0,1,0};//dx[]、dy[]数组用来搜索下一步 
//(x, y-1),(x-1, y),(x, y+1),(x+1, y)
 
struct route{//保存路线的结构体,每点有x、y坐标 
	int x,y;
}route[5010];

void DFS(int x,int y,int num){//num代表第num条路线 
	if(x==endx && y==endy){//到达终点,输出路线 
		for(int i=0;i<num;i++){
			cout<<"("<<route[i].x<<","<<route[i].y<<")->";
		}
		cout<<"("<<x<<","<<y<<")"<<endl;//最后一点为终点 
		no_route = false;
		return;
	}
	route[num].x = x,route[num].y = y;//起点赋值 
	for(int i=0;i<4;i++){//从起点开始从路线上每点四周递归遍历
	//如果此次1遍历试行可行且没越界,则递归遍历 
		if(a[x+dx[i]][y+dy[i]] == 1 && 1 <= x+dx[i] <= m && 1 <= y+dy[i] <= n){
			a[x][y] = 0;//遍历过不可再访问 
			DFS(x+dx[i],y+dy[i],num+1);//递归 
			a[x][y] = 1;//退出本次遍历也需将该点恢复可访问状态 
		}
	}
}

int main(){
	while(cin>>m>>n){//输入迷宫地图,下标从1开始 
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++){
				cin>>a[i][j];
			}
		}
		int startx,starty;//输入起始点与终点 
		cin>>startx>>starty>>endx>>endy;
		DFS(startx,starty,0);
		if(no_route)	cout<<-1<<endl;
	} 
	return 0;
}
	

DFS小结

DFS核心思想在递归,通过不断向深层搜索获取遍历序列,经典也面临重复问题,多采用剪枝改进;应用于八皇后、深度遍历、序列求解等

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!