牛客周赛 Round 89——小红开灯
题目
题解
这题是一道树形dp题,关键的难点就在于状态方程该如何考虑,我们可以设dp[N][3],如果当前点u操作,会和他的父亲一起染色,因为父亲只有一个,儿子不一定只有一个,选择哪个儿子染可能会导致更难的问题出现。我们可以发现一种方案,如果一个点被染过色,那么他不需要被再次改变颜色,对于此题,两次开关相当于没有任何操作,所以至多改变一次是最优操作。由此我们可以得出来两个结论
如果点u进行操作,那么他的儿子不可能进行操作。
如果点u不进行操作,他的儿子如果全被染色了,那一定是他们儿子的儿子干的。
由此我们可以得出状态表示的含义以及状态转移方程。
f[u][0] u进行操作,u和u的父亲染色。
由于结论1可知,他不可能由f[v][0]而来,但他均可从其他两个状态转移过来。故状态方程为f[u][0] += min(f[v][1], f[v][2]);
f[u][1] u不进行操作,但他其中一个儿子进行操作,导致u的其中一个儿子和u本身染色。
他由其中一个f[v][0]转移过来,对于其他儿子,f[v][1]、f[v][2]操作也均可,我们需要找出最小的那个f[v][0],这便是这道题的最难点所在。我们可以先进行f[u][1] += min(f[v][1], f[v][2])操作,然后再找出最小的f[v][0] - min(f[v][1], f[v][2]) 给他加回去,那么我们就将他染色的来源变成了f[v][0]。详见代码。
f[u][2] u不进行操作,他的所有儿子也均未操作,但是题目又不能有相邻两个亮的状态,那么就说明是由于他的儿子是由于他儿子的儿子导致染色也就是f[v][1], 状态转移方程为 f[u][2] += f[v][1]。
这样子我们就解决easy版了,只需找到f[1][1]和f[1][2]中的最小值输出即可。但对于hard版还要输出他的操作方案,我们就需要记录他从哪些为0的位置操作的。所以我们要记录f[u][1]时是由哪个儿子导致的u变色。然后我们再dfs一遍,遇到0就把他和他的父亲存起来,然后接着往下找就行了,最后输出即可。下面直接呈现hard版代码。
参考代码
#include<iostream>
#include<vector>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
#define int long long
int n;
vector<int> g[N];
int f[N][3], son[N];
vector<PII> ans;
void dfs(int u, int fa) {
f[u][0] = 1;
int ma = 1e9, id;
for(auto v : g[u]) {
if(v == fa) continue;
dfs(v, u);
f[u][0] += min(f[v][1], f[v][2]);
f[u][1] += min(f[v][1], f[v][2]);
f[u][2] += f[v][1];
if(ma > f[v][0] - min(f[v][1], f[v][2])) {
ma = f[v][0] - min(f[v][1], f[v][2]);
id = v;
}
}
if(g[u].size() != 1 || u == 1) f[u][1] += ma, son[u] = id;
if(g[u].size() == 1 && u != 1) f[u][1] = 1e9;
}
void dfs2(int u, int fa, int k) {
if(k == 0) ans.push_back({u, fa});
for(auto v : g[u]) {
if(v == fa) continue;
if(k == 0) {
if(f[v][1] < f[v][2]) dfs2(v, u, 1);
else dfs2(v, u, 2);
} else if(k == 1) {
if(v == son[u]) dfs2(v, u, 0);
else {
if(f[v][1] < f[v][2]) dfs2(v, u, 1);
else dfs2(v, u, 2);
}
} else dfs2(v, u, 1);
}
}
signed main() {
cin >> n;
for(int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, -1);
cout << min(f[1][1], f[1][2]) << endl;
if(f[1][1] < f[1][2]) dfs2(1, -1, 1);
else dfs2(1, -1, 2);
for(auto u : ans) cout << u.first << " " << u.second << endl;
return 0;
}

浙公网安备 33010602011771号