1 基础知识
暂无。。。
2 模板
暂无。。。
3 工程化
题目1:树的最长路径。
解题思路:遍历从根结点到叶子结点的最长距离和次长距离,注意遍历每一个下一步,因此避免了这两个路径有重叠。更新res,即res = max(res, d1 + d2)
。返回最长距离d1
。
C++代码如下,
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int n;
unordered_map<int, vector<pair<int,int>>> g;
int res = 0;
int dfs(int a, int fa) {
int d1 = 0;//d1表示最大长度
int d2 = 0;//d2表示次大长度
for (auto [b,c] : g[a]) {
if (b == fa) continue;
int d = dfs(b, a) + c;
if (d >= d1) {
d2 = d1;
d1 = d;
} else if(d > d2) {
d2 = d;
}
}
res = max(res, d1 + d2);
return d1;
}
int main() {
cin >> n;
for (int i = 0; i < n - 1; ++i) {
int a, b, c;
cin >> a >> b >> c;
g[a].emplace_back(b,c);
g[b].emplace_back(a,c);
}
dfs(1, -1);
cout << res << endl;
return 0;
}
题目2:树的中心。
解题思路:暴力解法,遍历每一个结点,以该结点为根结点,计算到叶子结点的最长路径。
C++代码如下,
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int n;
unordered_map<int,vector<pair<int,int>>> g;
int dfs(int a, int fa) {
int d = 0;
for (auto [b, c] : g[a]) {
if (b == fa) continue;
d = max(d, dfs(b, a) + c);
}
return d;
}
int main() {
cin >> n;
for (int i = 0; i < n - 1; ++i) {
int a, b, c;
cin >> a >> b >> c;
g[a].emplace_back(b,c);
g[b].emplace_back(a,c);
}
int res = 0x3f3f3f3f;
for (int i = 1; i <= n; ++i) {
res = min(res, dfs(i, -1));
}
cout << res << endl;
return 0;
}
题目3:数字转换。
解题思路:先计算出数i的除去本身的约数之和j,然后j到i建立一条边。然后求树中的最长路径。注意,所有数最终一定会转到1上,所以1是根结点,且只有一颗树。
C++代码如下,
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
const int N = 50010;
int sum[N];
unordered_map<int, vector<int>> g;
bool st[N];
int res = 0;
int dfs(int a) {
//返回以a为起点的最长路径。注意路径是指边。
st[a] = true;
int d1 = 0;//树中以a为起点的最长路径。注意路径是指边。
int d2 = 0;//树中以a为起点的次长路径。
for (int b : g[a]) {
if (st[b]) continue; //走过的结点不能再走
int d = dfs(b) + 1;
if (d > d1) d2 = d1, d1 = d;
else if (d > d2) d2 = d;
}
res = max(res, d1 + d2);
return d1;
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
//i是哪些j的约数,且j不等于i
for (int j = i + i; j <= n; j += i) {
sum[j] += i;
}
}
for (int i = 1; i <= n; ++i) {
int j = sum[i]; //j表示i除去本身的约数之和
//建立一条边j->i
if (j == 0) continue; //特判0,这个特殊情况。因为题目中要求转换过程中,必须都是正整数。
if (j >= i) continue; //建立这条边的条件,除去本身的约数之和小于本身
g[j].emplace_back(i);
}
dfs(1); //转换过程中肯定会指向1嘛?一定!
cout << res << endl;
return 0;
}
题目4:二叉苹果树。
解题思路:状态计算那儿有些没理解。
C++代码如下,
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
const int N = 110;
int n, m;
unordered_map<int, vector<pair<int,int>>> g; //first是结点,second是权重
int f[N][N];
void dfs(int a, int fa) {
for (auto [b, c] : g[a]) {
if (b == fa) continue;
dfs(b, a);
for (int j = m; j >= 0; --j) {
for (int k = 0; k + 1 <= j; ++k) {
f[a][j] = max(f[a][j], f[a][j - k - 1] + f[b][k] + c);
}
}
}
return;
}
int main() {
cin >> n >> m;
for (int i = 1; i < n; ++i) {//n-1条边
int a, b, c;
cin >> a >> b >> c;
g[a].emplace_back(b,c);
g[b].emplace_back(a,c);
}
dfs(1, -1);
cout << f[1][m] << endl;
return 0;
}
题目5:战略游戏。
状态定义f[i][0]
:表示在以结点i为根的子树中选择,且不选择结点i的case。该情况下的最小值。
状态定义f[i][1]
:表示在以结点i为根的子树中选择,且选择结点i的case。请情况下的最小值。
状态转移,
f
[
i
]
[
0
]
=
m
i
n
(
f
[
i
]
[
j
]
,
∑
i
f
[
s
i
]
[
1
]
)
f[i][0] = min(f[i][j],\ \ \sum_if[s_i][1])
f[i][0]=min(f[i][j], i∑f[si][1])
f
[
i
]
[
1
]
=
m
i
n
(
f
[
i
]
[
j
]
,
∑
i
m
i
n
(
f
[
s
i
]
[
0
]
,
f
[
s
i
]
[
1
]
)
)
f[i][1] = min(f[i][j], \ \ \sum_imin(f[s_i][0], \ \ f[s_i][1]))
f[i][1]=min(f[i][j], i∑min(f[si][0], f[si][1]))
其中结点i可以走到
s
i
s_i
si。
C++代码如下,
#include <iostream>
#include <unordered_map>
#include <vector>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1510;
int n;
unordered_map<int, vector<int>> g;
bool st[N];
int f[N][2];
void dfs(int a) {
f[a][0] = 0;
f[a][1] = 1;
for (auto b : g[a]) {
dfs(b);
f[a][0] += f[b][1];
f[a][1] += min(f[b][0], f[b][1]);
}
return;
}
int main() {
while (scanf("%d", &n) != -1) {
//多组测试数据,初始化相应变量
memset(st, 0, sizeof st);
memset(f, 0, sizeof f);
g.clear();
for (int i = 0; i < n; ++i) {
int a, cnt;
scanf("%d:(%d)", &a, &cnt);
for (int j = 0; j < cnt; ++j) {
int b;
scanf("%d", &b);
//a->b存在一条有向边
g[a].emplace_back(b);
st[b] = true; //b不是根结点
}
}
int root = -1;
for (int i = 0; i < n; ++i) {
if (!st[i]) {
root = i;
break;
}
}
dfs(root);
cout << min(f[root][0], f[root][1]) << endl;
}
return 0;
}
题目6:皇宫看守。
解题思路:暂时没有看懂。
C++代码如下,
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
const int N = 1510;
int n;
int w[N]; //w[i]表示在结点i安排守卫的费用
bool st[N]; //st[i]=true表示i不是根结点
unordered_map<int, vector<int>> g;
int f[N][N];
void dfs(int a) {
f[a][2] = w[a];
int sum = 0;
for (auto b : g[a]) {
dfs(b);
f[a][0] += min(f[b][1], f[b][2]);
f[a][2] += min(min(f[b][0], f[b][1]), f[b][2]);
sum += min(f[b][1], f[b][2]);
}
f[a][1] = 1e9;
for (auto b : g[a]) {
f[a][1] = min(f[a][1], sum - min(f[b][1], f[b][2]) + f[b][2]);
}
return;
}
int main() {
cin >> n;
for (int i = 0; i < n; ++i) {
int a, wi, cnt;
cin >> a >> wi >> cnt;
w[a] = wi;
for (int j = 0; j < cnt; ++j) {
int b;
cin >> b;
g[a].emplace_back(b);
st[b] = true;
}
}
int root = -1;
for (int i = 1; i <= n; ++i) {
if (!st[i]) {
root = i;
break;
}
}
dfs(root);
cout << min(f[root][1], f[root][2]) << endl;
return 0;
}