目录
洛谷P1219 [USACO1.5]八皇后 Checker Challenge
DFS深度优先搜索
定义:
暴力枚举
统计回文子序列
解题思路:
根据题目要求我们对于字符串中的每个元素都有选或不选两种状态,通过dfs遍历层数,递归出口是最后一个字符,每一次选择了就将字符添加进字符串,做完操作后删除,否则就直接进入下一层不添加,到达递归出口判断是否为回文串即可
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e9 + 5;
string a, b, c;
int n, ans = 0;
void dfs(int st)
{
//当到达序列最后位置就退出
if(st == n)
{
//将目前选择了的元素进行反转后判断是否为回文串
c = b;
reverse(c.begin(), c.end());
//是就+1否就+0
ans += (c == b);
return ;
}
//未选择的情况
dfs(st+1);
//选择了的情况
//选择了就将元素添加进暂存字符串
b += a[st];
dfs(st+1);
//做完选择了的操作就将这个元素从暂存字符串中删除
b.pop_back();
}
int main()
{
cin >> a;
n = a.size();
//用层数判断dfs的递归出口
dfs(0);
cout << ans - 1;
return 0;
}
输出全排列
输入一个数n,求1-n的全部排列方式
解题思路:
根据题意可以思考到可以用dfs深搜完成,将每个点作为开头,用一个临时存储字符串,在每一次遍历完1-n后就输出一次,每一次使用一个点就进行标记并添加进临时字符串,避免出现重复排列
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 100;
int n, ans = 0;
int book[maxn];
string a = "";
void dfs(int st)
{
//通过层数控制递归出口,st最多n层(0到n+1)
//每一次到达出口就输出在途中所得到的拼接字符串
if(st == n)
{
cout << a << endl;
return ;
}
//每一个数都作为开头尝试一次
for(int i = 1; i <= n; i++)
{
//如果此点已用过就返回循环开头
if(book[i])continue;
//如果此点没用过就标记此点
book[i] = 1;
//将当前点的数组转换成字符形式加入字符串
a += i + '0';
//进入下一层
dfs(st+1);
//当做完上一步操作后就将之前加入的点进行删除并取消标记
a.pop_back();
book[i] = 0;
}
}
int main()
{
cin >> n;
//传入层数0
dfs(0);
return 0;
}
技巧:next_permutation()
这个函数相当于是对于左地址与右地址后一位(左闭右开)进行从小到大的字典序排序,每一次都会慢慢变大,直到最大就返回false
#include<bits/stdc++.h>
usingnamespacestd;
#definelllonglong
constintmaxn=3e5+5;
inta[maxn];
intmain()
{
int n;
cin>>n;
for(inti=1;i<=n;i++)a[i]=i;
do
{
for(inti=1;i<=n;i++)
cout<<a[i]<<"";
cout<<endl;
//只要a!={5,4,3,2,1}就返回true
//否则因为已经到字典序最大的排列了,就返回false
}while(next_permutation(a+1,a+1+n));
return 0;
}
整数分解系列问题
解题思路:
题目要求k个数,那么可以将k设置为深搜出口,同时每一次记录上一个元素的值,这样避免后数比前数小的情况,当添加一个元素,就将ans加上这个元素,在出口判断是否满足等于n,满足就加入答案集合进行去重,对于每个点都需要进行循环遍历一次
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 1e9 + 5;
int n, k, ans = 0;
string a;
set<string> q;
void dfs(int st, int last)
{
//用st控制是否满足k个数
if(st == k)
{
//如果满足ans等于n就进行添加进答案集合去重
if(ans == n)
q.insert(a);
return ;
}
//对于每一个点都有两种状态选或不选
for(int i = last; i <= n; i++)
{
//选择的情况
a += i + '0';
ans += i;
dfs(st+1, i);
a.pop_back();
ans -= i;
//不选择的情况
dfs(st, i+1);
}
}
int main()
{
cin >> n >> k;
dfs(0, 1);
for(auto i: q)
cout << i << endl;
return 0;
}
上题进阶:分成1-n个部分,求输出所有方案
解题思路:
只需要在进入dfs之前通过一个循环来控制层数k即可实现
上题进阶:分成1-n个部分,求输出所有方案且不可有重复数字
解题思路:
在上题基础上每一次都将i赋值为last + 1即可
洛谷P1036 [NOIP2002 普及组] 选数
解题思路:
根据题意,我们可以通过dfs从数组开头遍历到最后一位,对于每个数我们都可以选或不选,用一个sum存储当前层数的值,选就加上这个数再下一层,不选就下一层,到达最后一层进行判断,是否选择k个数,以及这个和是否为素数,如果条件都满足,答案数+1
#include <bits/stdc++.h>
using namespace std;
int n,k,ans=0;
int a[25];
//判断素数
int ss(int a)
{
for(int i = 2;i <= sqrt(a); i++)
if(a % i == 0)return 0;
return 1;
}
void dfs(int st, int k1, int sum)
{
//当遍历完数组后进行判断
if(st == n+1)
{
//是否选择了k个数
if(k1 != k)return ;
//是否为素数,是的话答案数+1
if(ss(sum))ans++;
return ;
}
//选了的情况
dfs(st+1, k1+1, sum+a[st]);
//没选的情况
dfs(st+1, k1, sum);
}
int main()
{
cin >> n >> k;
for(int i = 1;i <= n;i++)
cin >> a[i];
//1-n的所有数,k个数,当前的和
dfs(1, 0, 0);
cout << ans;
return 0;
}
洛谷P1219 [USACO1.5]八皇后 Checker Challenge
题目链接:洛谷P1219 [USACO1.5]八皇后 Checker Challenge
#include <iostream>
using namespace std;
const int maxn = 100;
//记录数组:行,列,左对角线,右对角线
int a[maxn],b[maxn], c[maxn],d[maxn];
int ans = 0, n;
void dfs(int st)
{
//遍历完所有行为出口
if(st == n+1)
{
ans++;
//前三个输出
if(ans <= 3)
{
for(int i = 1; i <= n; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
return ;
}
//枚举st行中所有列的位置
for(int i = 1; i <= n; i++)
{
//判断当前列位置上,列/左对角线/右对角线是否没有皇后存在
if(b[i] == 0 && c[st - i + n] == 0 && d[i + st] == 0)
{
//记录行的位置
a[st] = i;
//标记当前列
b[i] = 1;
//标记左对角线
c[st - i + n] = 1;
//标记右对角线
d[i + st] = 1;
//进入下一行
dfs(st + 1);
//将之前的标记清空
a[st] = 0;
b[i] = 0;
c[st - i + n] = 0;
d[i + st] = 0;
}
}
}
int main()
{
cin >> n;
dfs(1);
cout << ans;
return 0;
}