DFS/BFS
关于DFS其实本质就是,当path达到对应数量就输出!如果没有达到,那么看看哪些还可以选择,如果可以选择就插入path,标记不可选,然后递归,然后标记可用,然后弹出,就是这么一个套路。
DFS:达则可,否则遍历入标递标出。
BFS:队列不空出遍入。
排列数字
使用回溯法生成所有可能的排列
每次选择一个未使用过的数字加入当前排列
当排列长度达到 n 时,输出该排列
通过按顺序尝试数字,自然保证了字典序
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int n;
vector<bool> used;
vector<int> path;
void work(){
if(path.size()==n){//如果path中达到对应数目
for (int i = 0; i < n; i ++ ){
cout << path[i] << " ";
}
cout << endl;
return;
}
for (int i = 1; i <= n; i ++ ){//循环每一个第一个数字,然后加入path然后递归
if(used[i]==false){
path.push_back(i);
used[i]=true;
work();
used[i]=false;
path.pop_back();
}
}
}
int main()
{
cin >> n;
used.resize(n+1,false);
work();
return 0;
}
n皇后问题
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int n;
vector<int> path;//候选
vector<bool> y;//列可用
vector<bool> ipj;//i+j对角线
vector<bool> idj;//i-j对角线
void printq(int a){
for (int i = 1; i < a; i ++ ){
cout << ".";
}
cout << "Q";
for (int i = 1; i <= n-a; i ++ ){
cout << ".";
}
cout <<endl;
}
//标准格式,如果达到长度循环打印,返回
//然后就是遍历,如果符合条件加入并且标记,递归,结束则删除去除标记
void work(){
int i = path.size()+1;
if(path.size()==n){
for (int i = 0; i < n; i ++ ){
printq(path[i]);
}
cout << endl;
return;
}//每一行,选取哪个?
for(int j = 1; j <= n;j++){
if(y[j]&&ipj[i+j]&&idj[i-j+n]){//判断对应的可以使用,注意i-j可能是负数要+n转化
path.push_back(j);
y[j] = false;
ipj[i+j] = false;
idj[i-j+n] = false;
work();
//标记为true
y[j] = true;
ipj[i+j] = true;
idj[i-j+n] = true;
path.pop_back();
}
}
}
int main()
{
cin >> n;
y.resize(n+1,true);
ipj.resize(2*n+2,true);
idj.resize(2*n+2,true);//因为+n了这里的也要稍微改一下
work();
return 0;
}
走迷宫
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int n,m;
int A[100][100];
int dist[100][100];//到达目的地的距离
queue<pair<int,int>> pos;//使用队列是因为遍历的是之前的而不是走到底才往回走
int x,y,a,b;
int dx[4]={0,1,0,-1};
int dy[4]={-1,0,1,0};
int main()
{
cin >> n>>m;
for (int i = 0; i < n; i ++ ){
for (int j = 0; j < m; j ++ ){
cin >> A[i][j];
}
}
for (int i = 0; i < n; i ++ ){
for (int j = 0; j< m; j ++ ){
dist[i][j] = -1;
}
}
dist[0][0] = 0;
pos.push({0,0});
while(!pos.empty()){
x = pos.front().first;
y= pos.front().second;
pos.pop();
if(dist[n-1][m-1]!=-1){
cout <<dist[n-1][m-1];return 0;
}
for (int i = 0; i < 4; i ++ ){
a = x+dx[i]; b = y + dy[i];
if(a>=0&&a<n&&b>=0&&b<m&&A[a][b]==0&&dist[a][b]==-1){//重点四个判断,ab符合条件,A是可走,dist未曾记录
dist[a][b] = dist[x][y]+1;
pos.push({a,b});
}
}
}
return 0;
}
华容道
BFS的思路就是使用一个队列,先初始化第一个,然后对第一个进行各种操作,如果符合正常条件就加入队列,循环各种,然后每次取头,如果这个头代表的符合要求就直接输出结果。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
unordered_map<string,int> mi;
queue<int> idx;
char M[332000][10];
int now;
int nowx;
int cx[4] = {-1,0,1,0};
int cy[4] = {0,1,0,-1};
int cou=1;
int a,b;
int dist=1;
int getx(){
for (int i = 0; i < 9; i ++ ){
if(M[now][i]=='x')return i;
}
}
int check(){
for (int i = 0; i < 10; i ++ ){
M[cou][i] = M[now][i];
}
swap(M[cou][nowx],M[cou][a*3+b]);
if(mi.find(string(M[cou]))!=mi.end())return 0;
else return 1;
}
int main()
{
for (int i = 0; i < 9; i ++ ){
cin>>M[0][i];
}
M[0][9]='\0';
mi[string(M[0])] = 0;
idx.push(0);
while(!idx.empty()){
now = idx.front();//
idx.pop();
nowx = getx();
if(string(M[now])=="12345678x"){cout << mi[string(M[now])];return 0;}
//cout << string(M[now])<<endl;
for (int i = 0; i < 4; i ++ ){
a = nowx/3+cx[i];
b = nowx%3+cy[i];//新的横纵
if(a>=0&&a<3&&b>=0&&b<3&&check()){
mi[string(M[cou])] = mi[string(M[now])]+1;
idx.push(cou);
cou++;
}
}
}
cout << -1;
return 0;
}
树的重心
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
vector<vector<int>> edge;
vector<int> sizee;
int min_max_subtree=100100;
int n;
void work(int son, int fat){
sizee[son] = 1;
int max_subtree = 0;
for(int x:edge[son]){
if(x==fat)continue;
work(x,son);//遍历每个边都递归,记录size,并且筛选最大的连通
sizee[son] += sizee[x];
max_subtree = max(max_subtree,sizee[x]);//针对当前循环下的子树
}
max_subtree = max(max_subtree, n - sizee[son]);//针对当前函数的子数,与父方向的比较
min_max_subtree = min(min_max_subtree,max_subtree);//找到最小的联通
}
int main()
{
int a,b;
cin >> n;
edge.resize(n+1);
sizee.resize(n+1);
for (int i = 0; i < n-1; i ++ ){
cin >> a >> b;
edge[a].push_back(b);
edge[b].push_back(a);
}
work(1,-1);//从1开始其实从哪里开始都一样
cout << min_max_subtree;
return 0;
}
图中点的层次
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
int n,m;
vector<vector<int>> edge;
int a,b;
vector<int> dist;
queue<int> path;
int now;
vector<int> res;
vector<int> visit;
int main()
{
cin >> n >> m;
edge.resize(n+1);
dist.resize(n+1);
visit.resize(n+1);
for (int i = 0; i <= n; i ++ ){
dist[i]=0;
visit[i]=0;//初始化
}
for (int i = 0; i < m; i ++ ){
cin >> a >> b;
edge[a].push_back(b);
}
path.push(1);
while(!path.empty()){
if(dist[n]){
res.push_back(dist[n]);//如果有到n的就加入结果
dist[n]=0;
}
now = path.front();
path.pop();
for(int x:edge[now]){
if(x==now||visit[x])continue;
path.push(x);//遍历每一个,加入结果并且记录距离
dist[x] = dist[now]+1;
visit[x]=1;
}
}
if(n==1){cout<<0;return 0;}//如果n=1直接就是0
if(res.size()){//多个结果找最小
int r=res[0];
for(int x:res){
r = min(r,x);
}
cout << r;
return 0;
}
cout << -1;//都没有输出-1
return 0;
}
拓扑排序
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
vector<vector<int>> edge;
//vector<vector<int>> inme;//使用入度数组,而不是进入的元素数组!
int n,m,a,b;
queue<int> path;
vector<int> res;
//还是BFS
int main()
{
cin >> n >> m;
edge.resize(n+1);
vector<int> indu(n+1,0);
//for (int i = 1; i <= n; i ++ ){
// visit[i]=0;
//}
for (int i = 0; i < m; i ++ ){
cin >> a >> b;
edge[a].push_back(b);
indu[b]++;
}
int head=-1;
for (int i = 1; i <= n; i ++ ){
if(!indu[i]){
path.push(i);
}
}
while(!path.empty()){
int now = path.front();
path.pop();
res.push_back(now);
for(int x:edge[now]){//还得考虑入度为0的情况,而不是都加入
indu[x]--;//抹除我的进入
if(now==x||indu[x])continue;
path.push(x);
}
}
if(res.size()==n){//处理不存在序列的情况,如果存在那么会达到n长
for(int x:res)cout << x << " ";
}
else{
cout << -1;
}
return 0;
}