目录
抽象DFS
抽象dfs特点: dfs看起来是图上完成的搜索算法,但是dfs的过程,我们没有看到图的存在
搜索树:
每个点有两个状态 选和不选 ,每个状态的子树
从n个数中选取k个数和为sum
样例输入:
5 3 9
1 2 3 4 5输出:
2
#include <iostream>
using namespace std;
int n_01,k_01,sum_01,ans_01;
int a_01[40];
void dfs_01(int i,int cnt,int s) { // 当前取第i个数 , cnt为取了几个数 ,s表示当前的和
if(i == n_01) { //每次搜索完判断
if(cnt == k_01 && s == sum_01) { //选了k个数 ,且和为sum
ans_01++;
}
return; //减少时间复杂度
}
dfs_01(i + 1, cnt , s);
dfs_01(i + 1, cnt + 1, s + a_01[i]); //s + a[i]表示为加上此步的和 ,递归到最后一个数n,判断此参数是否为sum
}
void test_01() {
//输入数据
cin>> n_01 >> k_01 >> sum_01;
for(int i = 0; i < n_01; i++) {
cin >> a_01[i];
}
ans_01 = 0;
dfs_01(0,0,0);
cout<<ans_01<<endl;
return;
}
/* 另一种做法 要除以阶乘
否则为12
*/
int n_02,k_02,ans_02,sum_02;
int a_02[110];
bool xuan_02[110]; //标记是否选择了
void dfs_02(int s,int cnt) {
if(s == sum_02 && cnt == k_02) {
ans_02++;
}
for(int i = 0; i < n_02; i++) {
if(!xuan_02[i]) {
xuan_02[i] = 1;
dfs_02(s + a_02[i],cnt + 1);
xuan_02[i] = 0;
}
}
}
void test_02() {
//输入数据
cin>> n_02 >> k_02 >> sum_02;
for(int i = 0; i < n_02; i++) {
cin >> a_02[i];
}
ans_02 = 0;
dfs_02(0,0);
cout<< ans_02 << endl;
return;
}
n根长度不同的木棍拼出等边三角形
输入n根木棍数 [3,10]
接下来一行输入每根木棍的长度 [1,10000]
每根木棍都要用到!
问是否能拼出样例输入:
5
1 2 3 4 5输出:
yes
#include<cstdio>
int p_03[15]; //木棍长度
int n_03,sum_03 = 0;
bool f_03;
bool vis[15];//标记木棍使用情况
void dfs_03(int p, int s, int st) { //当前拼成了p根边长 当前正在拼的木棍长度的和s ,选择从哪开始选木棍st , 有多种重复排列解
if(f_03) { //找到就返回
return;
}
if(p == 3) { //找到三个 木棍
f_03 = true;
return;
}
if(s == sum_03 / 3) { //和为sum_03 / 3 已经拼成一条边长了,考虑下一根
dfs_03(p + 1,0,0);//归为一个新的木棍 , 再从 p + 1 开始拼
return;
}
for(int i = 0; i < n_03; i++) {
if( !vis[i]) { //从没用过木棍开始遍历
vis[i] = true;//标记用过的木棍
dfs_03(p, s + p_03[i] , i + 1); //从第i根开始遍历
vis[i] = false;
}
}
}
//蓝桥杯不用文件时输入输出 !
// freoprnen("triangle.in","r",stdin);
// freoprnen("triangle.out","w",stdout);
void test_03() {
scanf("%d",&n_03);
for(int i = 0 ; i < n_03; i++) {
scanf("%d",&p_03[i]);//存入木棍长度
sum_03 += p_03[i]; //求木棍总长度
}
if(sum_03 % 3 != 0) { //先筛选判断,不能分成3等份直接 no ,就不用dfs了
printf("no\n");
} else {
dfs_03(0,0,0);
if(f_03) { //可以拼成三条相同长度的木棍
printf("yes\n");
} else {
printf("no\n");
}
}
return;
}
N皇后问题 (N个皇后不能处在同一行或同一列或同一斜线上)
主要问题 :斜线的标记如何判断 !
下标:
0 1 2 3 4 5 6 7
1
2
3
4
5
6
7发现规律:线主对角(y = -x) 行 - 列 == 定值1 , 副对角线 (y = x) 行 + 列 == 定值2
且每条对角线的差值均不同 ,利用此点判断标记对角线是否有重复的皇后
// 8改成n,输入n就可以计算n皇后的解
int ans_04 = 0;
bool col_04[10],x1_04[20],x2_04[20]; //col_04为列 , x1_04为第一条对角线,x2_04为第二条对角
bool check_04(int r,int i) { //行 ,每行所有位置
return !col_04[i] && !x1_04[r + i] && !x2_04[r - i + 8]; //行或列上没有标记 即满足不重复,才可以放置皇后
}
void dfs_04(int r) { //行循环标记
if(r == 8) { //填完8行,为一种情况ans_04++
ans_04++;
return;
}
for(int i = 0; i < 8; i++) {
if(check_04(r,i)) { //可以放 ,标记
col_04[i] = x1_04[r + i] = x2_04[r - i + 8] = true; //标记 第一条对角线x1_04 , 第二条对角线x2_04 ,r-i要 +8 是为了防止 r-i小于0
dfs_04(r + 1); //递归寻找
col_04[i] = x1_04[r + i] = x2_04[r - i + 8] = false; //为了列出所有满足n皇后的情况,一定要取消标记,重新判断 ,回退
}
}
}
void test_04() {
dfs_04(0);
cout<< ans_04 << endl;
return;
}
求一个n元高次方程的解
k1x1^p1 + k2x2^p2 + k3x3^p3 + k4x4^p4 +... + knxn^pn = 0; x∈[1,M] , i∈[1,n] , p ∈[1,4] , k ∈[-20,20]均为整数第一行输入一个整数n∈[1,4] ,
第二行输入一个整数M∈[1,150]
接下来 3 到 n + 2行输入对应位置的系数和幂
计算方程解的个数
样例输入:
3
100
1 2
-1 2
1 2
输出:
104
#include<cstdio>
int k_05[5] , p_05[5];
int n_05 , M_05 , ans_05;
long long poww_05(int x,int y) { //创次方函数 返回x^y ,注意数值大! long long 别用pow!!!
long long ret = 1;
for(int i = 0; i < y; i++) {
ret *= x;
}
return ret; //k_05[x]*返回的ret
}
void dfs_05(int x,long long s) { //表示每项 , x表示未知数 , s表示累加到当前多项式的值
if(x == n_05) { //遍历xn ,看是否满足方程
if(s == 0) {
ans_05++;
}
return;
}
for(int i = 1 ; i <= M_05; i++) { //未知数属于[1,M] ,遍历代入看是否满足方程
dfs_05(x + 1,s + k_05[x] * poww_05(i,p_05[x]) );//递归 x1 -- xn 判断当x1~xn 选 0-M (M*M种)时 项式值的和 是否 == 0
} //poww(i,p[x]) == ret return的返回值 ret
}
void test_05() {
scanf("%d",&n_05); //x1 -- xn n项式
scanf("%d",&M_05); //x取值范围 遍历
for(int i = 0; i < n_05; i++) {
scanf("%d%d",&k_05[i],&p_05[i]); //输入 系数 、幂 [写数组名仅代表第一个位置]
}
dfs_05(0,0); //x1--xn
printf("%d\n",ans_05);
/*测试
for(int i = 0;i < n;i++){
printf("%d %d\n",k_05[i],p[i]);
}
*/
return;
}
数独 (较难)
(每行或每列元素不重复 , 按九宫格分区,九宫格内为1-9不重复)输入一张数独表
输出填完后的数独表样例输入:
* 2 6 * * * * * *
* * * 5 * 2 * * 4
* * * 1 * * * * 7
* 3 * * 2 * 1 8 *
* * * 3 * 9 * * *
* 5 4 * 1 * * 7 *
5 * * * * 1 * * *
6 * * 9 * 7 * * *
* * * * * * * * *输出:
1 2 6 7 3 4 5 9 8
3 7 8 5 9 2 6 1 4
4 9 5 1 6 8 2 3 7
7 3 9 4 2 5 1 8 6
8 6 1 3 7 9 4 2 5
2 5 4 8 1 6 3 7 9
5 4 7 2 8 1 9 6 3
6 1 3 9 4 7 8 5 2
9 8 2 6 5 3 7 4 1
char s_06[10][10]; //用ASCLL 减少空间复杂度
bool f_06; //初始值 false 即 0
bool vx_06[10][10],vy_06[10][10],vv_06[10][10]; //vx_06为每行已经填的数字标记 ,vy_06为每列已经填的数字标记 , vv_06为九宫格3*3已经填的数字标记
void dfs_06(int x,int y) { //下标
//测试 printf("%d%d", x , y);
if(f_06) { //已经找到 dfs是不断回溯 返回值 ,f开全局能保存是否找到 ,找到不断后退到最初的dfs(0,0),return退出
return;
}
if(x == 9) { //到第九行 打印
f_06 = true;
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
if (j != 8) { //最后一个不留空格格式
printf("%c ",s_06[i][j]);
} else {
printf("%c \n",s_06[i][j]);
}
}
}
return;
}
if(y == 9) {
dfs_06(x + 1,0); //遍历行 ,每行从下标0 元素开始 直到最后一行
return;
}
if(s_06[x][y] != '*') {
dfs_06(x,y + 1);
return;
}
for(int i = 1; i <= 9; i++) { //遍历数字 1 - 9
if(!vx_06[x][i] && !vy_06[y][i] && !vv_06[x / 3 * 3 + y / 3][i] ) { //还未标记此数字 ,可以填入
s_06[x][y] = '0' + i; //用char数组 存储 ASCLL码 映射填入 1 - 9
vx_06[x][i] = true;
vy_06[y][i] = true;
vv_06[x / 3 * 3 + y / 3][i] = true; //填每个小九宫格里的位置,完 再作为大格子里的位置
dfs_06(x,y + 1);//遍历寻找合适值填入
vx_06[x][i] = false; //清除标记
vy_06[y][i] = false;
vv_06[x / 3 * 3 + y / 3][i] = false;
s_06[x][y] = '*';
}
}
}
void test_06() {
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) { //字符数组 单个读取!
scanf(" %c",&s_06[i][j]); // 注意输入率格式有空格 :空%c 的输入格式才是对的 (第一个空不管,每行是%c结束的)
}
}
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
//测试printf("%c ",s_06[i][j]);
if(s_06[i][j] != '*') { //已经填好的
vx_06[i][s_06[i][j] - '0'] = true; //s_06[i][j] - '0' 为了 算出填好的元素 ,记录vx_06行中使用过的数字
vy_06[j][s_06[i][j] - '0'] = true; //记录vy_06列中使用过的数字
vv_06[i / 3 * 3 + j / 3][s_06[i][j] - '0'] = true; //记录九宫格内使用过的数字
}
}//cout<<endl;
}
dfs_06(0,0); //填数独
return;
}
双色皇后
输入棋盘大小n (n*n)
接下来n行输入棋盘 0/1 (1表示可以放皇后 , 0则不行) (小细节:遇到0就不能放状态标记用0表示不能再放 ,黑的用1)
有n个黑皇后和n个白皇后放入棋盘, 均满足 同一颜色皇后不能处在同一行或同一列或同一斜线上
输出一个整数,表示有多少种放法样例输入:
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
输出:
2
int mp_07[10][10];
int vy_07[10],vd1_07[20],vd2_07[20]; // (行遍历不用vx) 需: 列 、 第一条对角线(行列和) 、第二条对角线(横列差+行数n 保证>0) 标记 !!!!!
int n_07,ans_07;
void dfs_07(int x,int p) { //p判断先放白 后放黑 每轮中填入p在判断数组vy_07,vd1_07,vd2_07中 ,1 代表填入白 ,2代表填入黑
if(x == n_07 && p == 2) { //两轮遍历完 x == n 表示有填完 有解
ans_07++;
return;
}
if(x == n_07) { //0不能放了,找下一轮
dfs_07(0,p + 1);
return;
}
//遍历判断 寻找能放的位置 (此处以x行固定遍历每行所有元素,再遍历x行 ,当然也可以用y列 )
for(int i = 0; i < n_07; i++) {
//题目mp_07中为0不能放 3表示 寻找递归结束后p再 +1 ,p为3 说明白、黑已经各遍历过了,应该结束了
//不能是p,p为1时放白,2时再放有标记为1的说明放白了,不能用 ==3,说明已经结束了不用再放了
if( mp_07[x][i] && vy_07[i] != 3 && vy_07[i] != p && vd1_07[x + i] != 3 &&vd1_07[x + i] != p && vd2_07[x - i + n_07] != 3 &&vd2_07[x - i + n_07] != p ) {
mp_07[x][i] = 0;//放入皇后标记状态(表示不能再放)
vy_07[i] += p;
vd1_07[x + i] += p;
vd2_07[x - i + n_07] += p;
dfs_07(x + 1, p);//递归
vy_07[i] -= p; //恢复状态(与状态改变前相等 ,之前 +p 现在就 -p),再次寻找ans_07
vd1_07[x + i] -= p;
vd2_07[x - i + n_07] -= p;
mp_07[x][i] = 1;
}
}
}
void test_07() {
scanf("%d",&n_07);
for(int i = 0; i < n_07; i++) {
for(int j = 0; j < n_07; j++) {
scanf("%d",&mp_07[i][j]); //输入格式问题! 空%d 或 %d 【%d空 ,表示空格结尾,导致要多输入一行】
}
}
dfs_07(0,1);
printf("%d\n",ans_07);
return;
}
引爆炸弹
n*m地图 放置这炸弹,手动引爆一个炸弹,会把炸弹所在的行和列的炸弹都引爆,连锁反应
求最少手动引爆多少个炸弹,可以把地图上的所有炸弹都引爆输入地图大小n,m ( 5为炸弹,0无炸弹)
输入地图
样例输入:5 5
00010
00010
01001
10001
01000输出:
2
int n_08,m_08,cnt_08;
char s_08[1005][1005]; //字符串读效率高,减少时间复杂度
bool vx_08[1005],vy_08[1005];
void dfs_08(int x,int y) {
s_08[x][y] = '0'; //找到炸弹,清为0 ,避免重复搜索
if(!vx_08[x]) { //遍历行
vx_08[x] = true;
for(int i = 0; i < m_08; i++) {
if(s_08[x][i] == '1') {
dfs_08(x,i);
}
}
}
if(!vy_08[y]) { //遍历列
vy_08[y] = true;
for(int i = 0; i < n_08; i++) {
if(s_08[i][y] == '1') {
dfs_08(i,y);
}
}
}
}
void test_08() { //O(n*m)
scanf("%d%d",&n_08,&m_08);
for(int i = 0; i < n_08; i++) {
scanf("%s",s_08[i]); //格式为每行连续 可为字符串 减少读取时间
}
for(int i = 0; i < n_08; i++) {
for(int j = 0; j < m_08; j++) {
if(s_08[i][j] == '1') {
cnt_08++;
dfs_08(i,j);
}
}
}
printf("%d\n",cnt_08);
return;
}
int main() {
test_08();
return 0;
}
八皇后的解统计
数独