DFS深搜小谈
前几天有人跟我说,啊,说dfs一搜搜着搜着就把自己搜蒙了,说一写dfs就要dfs(int a,int b,int c),括号里面放一堆东西。啊今天我要澄清一下,dfs其实没有你想的那么复杂。
dfs这个玩意其实是有模板的:
//DFS模板
inline void dfs(int u){
if(u==题目中的某个条件){
按照题目要求单组输出
puts("");
return ;
}
for(register int i=___;i<___;i___){
if(!st[i]%%____________){
st[i]=true;
按照题目要求操作
回溯//e.g. dfs(u+1);
st[i]=false;//回复现场
//path[u]=0;
}
}
}
举个例子:
给定一个整数 n,将数字 1~n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
就这道题,我们用模板解一下
#include<bits/stdc++.h>
using namespace std;
int n;
int path[10000];
bool st[10000];
inline void dfs(int u){
if(u==n){
for(register int i=0;i<n;i++){
cout<<path[i]<<" ";
}
puts("");
return ;
}
for(register int i=0;i<n;i++){
if(!st[i]){
st[i]=true;
path[u]=i+1;
dfs(u+1);
st[i]=false;
path[u]=0;
}
}
}
int main(){
cin>>n;
dfs(0);
return 0;
}
你看,轻轻松松,那有人问了,每道题都能这么解吗???万一要dfs好多数呢???大部分其实只要一个数就行,我举个栗子:
n皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,
使得皇后不能相互攻击到,
即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 n,请你输出所有的满足条件的棋子摆法。
#include <iostream>
using namespace std;
const int N = 10;
int n;
bool row[N], col[N], dg[N * 2], udg[N * 2];
char g[N][N];
void dfs(int x, int y, int s)
{
if (s > n) return;
if (y == n) y = 0, x ++ ;
if (x == n)
{
if (s == n)
{
for (int i = 0; i < n; i ++ ) puts(g[i]);
puts("");
}
return;
}
g[x][y] = '.';
dfs(x, y + 1, s);
if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])
{
row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;
g[x][y] = 'Q';
dfs(x, y + 1, s + 1);
g[x][y] = '.';
row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
}
}
int main()
{
cin >> n;
dfs(0, 0, 0);
return 0;
}
大部分人肯定这么做的,但是,你用初中数学算一算,就可以进行改进:
#include<bits/stdc++.h>
using namespace std;
const int N=3e3;
int n;
char g[N][N];
bool col[N],dg[N],udg[N];
void dfs(int u){
if(u==n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)
cout<<g[i][j];
cout<<endl;
}
puts("");
return;
}
for(int i=0;i<n;i++)
if(!col[i]&&!dg[u+i]&&!udg[n-u+i]){
g[u][i]='Q';
col[i]=dg[u+i]=udg[n-u+i]=true;
dfs(u+1);
col[i]=dg[u+i]=udg[n-u+i]=false;
g[u][i]='.';
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[i][j]='.';
dfs(0);
return 0;
}
效果是一样的,但是不需要去想dfs后面要几个数,
dfs(int u)
这一个就够了啊
当然,在图中的dfs也是这个道理。
给定一颗树,树中包含 n 个结点(编号 1~n)和 n-1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 n,表示树的结点数。
接下来 n+1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。
输出格式
输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 2;
int n;
int h[N], e[M], ne[M], idx;
int ans = N;
bool st[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int dfs(int u)
{
st[u] = true;
int size = 0, sum = 0;
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (st[j]) continue;
int s = dfs(j);
size = max(size, s);
sum += s;
}
size = max(size, n - sum - 1);
ans = min(ans, size);
return sum + 1;
}
int main()
{
scanf("%d", &n);
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1);
printf("%d\n", ans);
return 0;
}