文章目录
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核心思想在递归,通过不断向深层搜索获取遍历序列,经典也面临重复问题,多采用剪枝改进;应用于八皇后、深度遍历、序列求解等
来源:CSDN
作者:李霁明
链接:https://blog.csdn.net/qq_34767784/article/details/103911623