赞
踩
DFS俗称暴搜,要考虑顺序。
用一个什么样的顺序来枚举所有方案。
画成一棵树来理解,回溯的时候要记得恢复现场。
树是一种特殊的图,有向图存一条边,无向图存两条边。
图可以通过:邻接矩阵(不常用),邻接表(常用)。
邻接表:插入节点用头插法。
记得初始化h
memset(h, -1, sizeof h);
void add(int a, int b, int c)
{
//h[N]邻接表存储树,有n个结点,所以需要n个队列头结点
//e[M]存储每个结点的值是多少
//ne[M]存储每个结点的next指针是多少
//e和ne通过下标来联系
//w[M]每条边的权重
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 n,表示树的结点数。
接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。
输出格式
输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
1≤n≤105
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
解题思路:
//头插法模板 void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } //dfs模板 void dfs(int u) { state[u] = true; for(int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if(!state[i]) dfs(j); } }
代码
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N = 1e5 + 10; int n, idx, ans = N; //idx是当前数组的下标 int h[N], e[2 * N], ne[2 * N]; bool state[N]; void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } int dfs(int u) //dfs函数是求u结点子树的最大值,含u结点 { int size = 0, sum = 1; //size是最大子树结点个数,不含u结点,sum是函数返回值 state[u] = true; for(int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if(!state[j]) { int s = dfs(j); //求一下该结点数量 size = max(size, s); //取所有子树的最大值 sum += s; //求该结点和子树所有的点的数量 } } size = max(size, n - sum); ans = min(size, ans); return sum; } int main() { cin >> n; memset(h, -1, sizeof h); for(int i = 0; i < n; i++) { int a, b; cin >> a >> b; add(a, b), add(b, a); //树是无向图,存两条边 } dfs(1); cout << ans << endl; return 0; }
n− 皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 n,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数 n。
输出格式
每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。
其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
注意:行末不能有多余空格。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1≤n≤9
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
解题思路:
代码
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N = 10; int n; char g[N][N]; bool col[N], dg[2 * N], udg[2 * N]; void dfs(int u) { if(u == n) { for(int i = 0; i < n; i++) cout << g[i] << 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; }
很久以前,T 王国空前繁荣。
为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。
同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J 是 T 国重要大臣,他巡查于各大城市之间,体察民情。
所以,从一个城市马不停蹄地到另一个城市成了 J 最常做的事情。
他有一个钱袋,用于存放往来城市间的路费。
聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。
具体来说,一段连续的旅途里,第 1 千米的花费为 11,第 2 千米的花费为 12,第 3 千米的花费为 13,…,第 x 千米的花费为 x+10。
也就是说,如果一段旅途的总长度为 1 千米,则刚好需要花费 11,如果一段旅途的总长度为 2 千米,则第 1 千米花费 11,第 2 千米花费 12,一共需要花费 11+12=23。
J 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式
输入的第一行包含一个整数 n,表示包括首都在内的 T 王国的城市数。
城市从 1 开始依次编号,1 号城市为首都。
接下来 n−1 行,描述 T 国的高速路(T 国的高速路一定是 n−1 条)。
每行三个整数 Pi,Qi,Di,表示城市 Pi 和城市 Qi之间有一条双向高速路,长度为 Di 千米。
输出格式
输出一个整数,表示大臣 J 最多花费的路费是多少。
数据范围
1≤n≤105,
1≤Pi,Qi≤n,
1≤Di≤1000
输入样例:
5
1 2 2
1 3 1
2 4 5
2 5 4
输出样例:
135
解题思路:树:有向无环图
树中长度最长的路径:树的直径
求法:
- 任取一点x, disc[] ---- 最大值点y
- 找到距离x最远的点y, disc[] ---- 最大值就是直径
代码
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; const int N = 1e5 + 10; int n, ans; int e[N * 2], ne[N * 2], w[N * 2], h[N], idx; int dist[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } void dfs(int u, int father, int distance) { dist[u] = distance; for(int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if(j != father) dfs(j, u, distance + w[i]); } } int main() { memset(h, -1, sizeof h); scanf("%d", &n); for(int i = 0; i < n - 1; i++) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c); add(b, a, c); } dfs(1, -1, 0); int u = 1; for(int i = 1; i <= n; i++) if(dist[i] > dist[u]) u = i; dfs(u, -1, 0); for(int i = 1; i <= n; i++) if(dist[i] > dist[u]) u = i; int s = dist[u]; printf("%lld\n", s * 10 + s * (s + 1ll) / 2); return 0; }
听说最近两斑点的奶牛最受欢迎,约翰立即购进了一批两斑点牛。
不幸的是,时尚潮流往往变化很快,当前最受欢迎的牛变成了一斑点牛。
约翰希望通过给每头奶牛涂色,使得它们身上的两个斑点能够合为一个斑点,让它们能够更加时尚。
牛皮可用一个 N×M 的字符矩阵来表示,如下所示:
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
其中,X 表示斑点部分。
如果两个 X 在垂直或水平方向上相邻(对角相邻不算在内),则它们属于同一个斑点,由此看出上图中恰好有两个斑点。
约翰牛群里所有的牛都有两个斑点。
约翰希望通过使用油漆给奶牛尽可能少的区域内涂色,将两个斑点合为一个。
在上面的例子中,他只需要给三个 . 区域内涂色即可(新涂色区域用 ∗ 表示):
................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....
请帮助约翰确定,为了使两个斑点合为一个,他需要涂色区域的最少数量。
输入格式
第一行包含两个整数 N 和 M。
接下来 N 行,每行包含一个长度为 M 的由 X 和 .构成的字符串,用来表示描述牛皮图案的字符矩阵。
输出格式
输出需要涂色区域的最少数量。
数据范围
1≤N,M≤50
输入样例:
6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
输出样例:
3
解题思路:dfs遍历两个斑点的所有坐标,存到vector中,然后枚举两个斑点的两个点
#include<iostream> #include<algorithm> #include<cstring> #include<vector> #define x first #define y second using namespace std; typedef pair<int, int> PII; const int N = 55; int n, m; vector<PII> points[2]; char g[N][N]; int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1}; void dfs(int a, int b, vector<PII> &points) { points.push_back({a, b}); g[a][b] = '.'; for(int i = 0; i < 4; i++) { int x = a + dx[i], y = b + dy[i]; if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 'X') dfs(x, y, points); } } int main() { int k = 0, ans = 110; cin >> n >> m; for(int i = 0; i < n; i++) cin >> g[i]; for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) if(g[i][j] == 'X') dfs(i, j, points[k++]); for(auto a : points[0]) for(auto b : points[1]) ans = min(ans, abs(a.x - b.x) + abs(a.y - b.y)); cout << ans - 1 << endl; return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。