最近新接触的一些DFS的题目

树的重心(这题挺难,这个代码不好理解)

题目链接–树的重心

dfs函数解析

int dfs(int x)
{
    
    
	vis[x] = true;
	int sum = 1, cnt = 0;
	for (int i = h[x]; i != -1; i = ne[i])
		if (!vis[e[i]]) {
    
    
			int s = dfs(e[i]);//s求得的是以e[i]为根的一条分支上节点的个数(不包括e[i])
			cnt = max(cnt, s);//cnt存的是将连接e[i]的点断开后每条分支上节点个数的最大值
			sum += s;//每得一分支得节点数都会加到sum上,包括当前节点e[i]
		}
	cnt = max(n - sum, cnt);
	siz = min(siz, cnt);
	return sum;
}

关于dfs这个函数举个栗子应该就能明白了:
比如dfs(1):开始是从节点1开始的,接着到节点4,探测完4的分支节点再比较n-sum(sum包括4节点)和cnt得到的就是去除4节点后的最大连通集的节点,在探测4节点过程中会到节点3和6,也能分别求出去掉3和6后的最大连通集的节点,所以虽然dfs是从节点1开始出发的,但依靠cnt=max(n-sum,cnt)和siz=min(siz,cnt)就把去掉每个节点后的最大连通集节点数求出来了。
那这里有个问题,为什么要cnt和n-sum比较,遍历完4的两个分支3和6在网上遍历4的最后一个分支1不就好了,但问题是,4就是从1来的,已经标记1节点访问过,所以没法再往上遍历,从1出发遍历的节点只能是1的三个分支的节点数。
1的分支有2 4 7,4已经遍历过了,也得到4(包括4)的根节点数,所以只要遍历2,7就行,在从1开始时sum就为1,那sum+=s,得到的就是整个树的节点数,相当于一个节点不删除,这个不影响,因为要求的时最大集合的最小值
因为每次到一个新的节点b都是从一个相连节点a来的(a是被删的节点),而a又是从一个节点c来的,所以求a邻接的分支的最大联通集合不包括来到a的c所代表的分支,所以需要再次比较,siz=max(siz,n-sum);

完整代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 5;
int e[2 * N], ne[2 * N], h[N], idx;
bool vis[N];
int siz = N;
int n;
void add(int x, int y)
{
    
    
	e[idx] = y;
	ne[idx] = h[x];
	h[x] = idx++;
}
int dfs(int x)//这一块的工作原理逐步调试就明白了
{
    
    
	vis[x] = true;
	int sum = 1, cnt = 0;
	for (int i = h[x]; i != -1; i = ne[i])
		if (!vis[e[i]]) {
    
    
			int s = dfs(e[i]);//s求得的是以e[i]为根的一条分支上节点的个数(不包括e[i])
			cnt = max(cnt, s);//cnt存的是将连接e[i]的点断开后每条分支上节点个数的最大值
			sum += s;//每得一分支得节点数都会加到sum上,包括当前节点e[i]
		}
	cnt = max(n - sum, cnt);
	siz = min(siz, cnt);
	return sum;
}
int main()
{
    
    
	int x, y;
	cin >> n;
	memset(h, -1, sizeof h);
	for (int i = 1; i < n; i++)
	{
    
    
		cin >> x >> y;
		add(x, y); add(y, x);
	}
	dfs(1);
	cout << siz;
	return 0;
}

在这里插入图片描述
芜湖
一道题看了好久,我好菜wuwuwu

N皇后问题(对角线处理很巧妙)

题目链接–n-皇后问题
思路很简单,和排序数字思路差不多
一个个枚举,每次放皇后已经能保证在不同行了,那么只要保证不同列,不同对角线就行。

if (!vis[i] && !dg[i + cnt] && !ndg[n + i - cnt])
这个处理对角线问题的方法很巧妙
两个式子y=x+b,y=-x+b
同一条对角线的斜率是相同的,所以如果在同一条对角线上dg[i+cnt]相同,这个对应y=-x+b
如果是y=x+b,b=y-x可能是负数,加个n偏移一下就好了,就是ndg[n+i-cnt]相同就表示在同一条对角线上
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 20;
char dot[N][N];
bool vis[N], dg[N], ndg[N];//同列,对角,反对角
int n;
void dfs(int cnt)
{
    
    
	if (cnt == n+1){
    
    
		for (int i = 1; i <= n; i++)
			cout << dot[i]+1 << endl;
		cout << endl;
		return;
	}
	for (int i = 1; i <= n; i++)//i表示列,cnt表示行
		//y=x+b,y=-x+b;
	{
    
    
		if (!vis[i] && !dg[i + cnt] && !ndg[n + i - cnt]){
    
    
			vis[i] = dg[i + cnt] = ndg[n + i - cnt] = true;
			//先告诉后面的递归该位置已经安排皇后了
			dot[cnt][i] = 'Q';
			dfs(cnt + 1);
			dot[cnt][i] = '.';
			vis[i] = dg[i + cnt] = ndg[n + i - cnt] = false;
			//恢复原样,在同行的下一个位置进行判断是否能放皇后
		}
	}
}
int main()
{
    
    
	cin >> n;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			dot[i][j] = '.';
	dfs(1);
	return 0;
}

排列数字

题目链接–排列数字

#include<iostream>
#include<algorithm>
using namespace std;
int path[10], n;
bool vis[10];
void dfs(int cnt)
{
    
    
	if (cnt == n) {
    
    
		for (int i = 0; i < n; i++)
			cout << path[i]<<" ";
		cout << endl;
		return;
	}
	for (int i = 1; i <= n; i++)
	{
    
    
		if (!vis[i])
		{
    
    
			vis[i] = true;
			path[cnt] = i;
			dfs(cnt + 1);
			vis[i] = false;
		}
	}
}
int main()
{
    
    
	cin >> n;
	dfs(0);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_50816938/article/details/119178101