CSP初赛复习-27-搜索与回溯

搜索

索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法

常见2种搜索遍历方式:宽度优先搜索(Breadth First Search)、深度优先搜索DFS (Depth-First-Search )

宽度优先搜索

宽度优先搜索是一种基于队列的搜索算法。它从起始节点开始,逐层地向外扩展搜索,直到找到目标节点或者搜索完整张图.

深度优先搜索

深度优先搜索是一种递归的搜索算法。它从起始节点开始,递归地访问每一个节点,直到找到目标节点或者搜索完整张图

搜索与回溯

是计算机解题中常用的算法,很多问题无法根据某种确定的计算法则来求解,可以利用搜索与回溯的技术求解。

回溯是搜索算法中的一种控制策略。

它的基本思想是:为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解

例1 可能体积

问题描述

给出n 件物品,每件物品有一个体积V[i] ,求从中取出若干件物品能够组成的不同的体积和有多少种可能

输入格式

第 1行1 个正整数,表示 n(n<=20)

第 2 行 n 个正整数,表示 V[i] (v[i]<=50),每两个数之间用一个空格隔开

输出格式

一行一个数,表示不同的体积和有多少种可能

输入样例

3 
1 3 4

输出样例

6

大致思路

1 枚举一个方案时,从第1个物品开始,都可以选择和不选择,这种情况对结合有不同影响

2 第1个物品选择时,第2个物品也有选择和不选择2中方案,第1个物品不选择时,第2个物品也有选择和不选择2种方案。

3 继续处理第3个物品...n个物品

4 走到第n个物品时回溯到第n-1个物品,n-1个物品处理完,继续回溯到n-2个物品...

参考程序

#include<bits/stdc++.h>
using namespace std;
int v[21];//n件物品体积 
bool h[1001];//体积去重 计数排序思想 
int n;//n个物品
/*
  dep 第几个物品
  已经装入物品的体积 
*/ 
void dfs(int dep,int sum){
    if(dep>n){//按一条路径遍历结束 出一个可行方案 n+1时退出 
        h[sum]=true;//一个方案求出的体积作为下标存入h数组 
        return;//产生一个方案 退出 
    }
    dfs(dep+1,sum+v[dep]);//本物品装入 
    dfs(dep+1,sum);//本物品不装 
}
int main(){
    cin>>n;//n件物品 
    for(int i=1;i<=n;i++){//输入n件物品对应体积 
        cin>>v[i];
    }
    dfs(1,0);// 从第1件物品开始 前面没有体积sum=0 
    int ans=0;//累加方案数 
    for(int i=1;i<=1000;i++){//可行的体积已存入h数组 重复也时1个 
        if(h[i]){//如果i下标的体积存入过 ans++ 
            ans++;
        }
    }
    cout<<ans<<endl;
} 
/*
输入 
3
1 3 4
输出 
6
*/ 

例2 01背包

问题描述

有n件物品,每件物品的重量为w[i],价值为c[i]. 现在需要选出若干件物品放入一个容纳重量为V的背包中,使得在选入背包的物品重量和不超过容纳重量V的前提下,让背包中物品的价值之和最大,求最大价值

输入格式

第 1行1 个正整数,表示 n(1<=n<=20)

第 2 行 n 个正整数,表示 w[i]

第 3 行 n 个正整数,表示 c[i]

输出格式

一行一个数,最大价值

输入样例

5 8
3 5 1 2 2
4 5 2 1 3

输出样例

10

大致思路

1 安排把物品一件一件放入背包,放入物品的由于选择价值最高,可以选择中间某件物品不放入形成一种方案,放入形成一种方案,最后结果对比,取价值高的

2 每件物品都可以选择放入和不放入,所以可以保证枚举所有情况,取价值最高的

3 每件物品都可以选择放入 和不放入背包 ,因此需要处理n次,第n+1次进行,判断取最大值

参考程序

#include<bits/stdc++.h>
using namespace std;
const int maxn=30;
int n,V,maxValue=0;//n件物品 V 背包容纳重量 
int w[maxn],c[maxn];//w[] 每家物品的重量 c[]每件物品的价值 

/*
  index 本次装第index件物品
  sumW 已装入背包的总重量 
  sumC已装入背包的总价值 
*/ 
void dfs(int index,int sumW,int sumC){
	if(index==n+1){//按一条路径遍历结束 出一个可行方案 n+1时退出 
	    //可以装入 并且本方案出现更高总价值 替换maxValue 
		if(sumW<=V && sumC>maxValue){ 
			maxValue=sumC;
		}
		return;
	}
	//走到岔路口(枚举装还是不装) 
	dfs(index+1,sumW,sumC);//不装sumW和 sumC不变  
	dfs(index+1,sumW+w[index],sumC+c[index]);//装 sumW和 sumC加入本次重量和价值 
}
int main(){
	scanf("%d%d",&n,&V);//输入n件物品和背包容纳重量V 
	for(int i=0;i<n;i++){//输入每件物品重量到w数组 
		scanf("%d",&w[i]);
	}
	for(int i=0;i<n;i++){//输入每件物品价值到c数组 sumW和sumC为0 
		scanf("%d",&c[i]);
	}
	//从第1个物品开始装入背包 此时背包没装任何物品 
	dfs(1,0,0); 
	printf("%d",maxValue);//输出总价值 
	return 0;
} 
/*
输入
5 8
3 5 1 2 2
4 5 2 1 3
输出
10 
*/

例3 全排列

问题描述

按照字典序输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字

输入格式

一个整数 n (1<=n<=9)

输出格式

由1~n 组成的所有不重复的数字序列,每行一个序列

每个数字保留5个场宽

输入样例

3

输出样例

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

大致思路

1 把n个数字填入输入row中,一位一位填,一直填到n位

2 再深入进行1位为n+1,此时输出本方案即可

3 一次路径结束后,回溯到上一位继续填入相应数字,继续深入到n+1位输出

4 输出过程由于本次过程中数字不能重复,需要一个数字used记录本次已填的数字

参考程序

#include<bits/stdc++.h>
using namespace std;
//used是判断是否用过这个数 row 本次方案存储的数 
int n,used[100],row[100];
void print(){//输出函数
    for(int i=1;i<=n;i++){
    	printf("%5d",row[i]);//保留五位常宽
	} 
    cout<<endl;
}
/*
  深搜函数,当前是填第k格
*/ 
void dfs(int k){
    if(k==n+1){//填满了的时候
        print();//输出当前解
        return;
    }
    for(int i=1;i<=n;i++){//1-n循环填数
        if(!used[i]){//如果当前数没有用过
            used[i]=1;//标记一下
            row[k]=i;//把这个数填入数组
            dfs(k+1);//填下一个
            used[i]=0;//回溯
        }
    }
}
int main(){
    cin>>n;
    dfs(1);//从数组第1个位置开始填 
    return 0;
}
/*
输入
3
输出
    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1 
*/ 

CSP初赛复习-27-搜索与回溯-练习题
https://www.cnblogs.com/myeln/articles/17625739.html

posted @ 2023-08-12 22:33  new-code  阅读(136)  评论(0)    收藏  举报