题目描述
有一棵 n 个节点的树,每条边长度为 1,颜色为黑或白。
可以执行若干次如下操作:选择一条简单路径,反转路径上所有边的颜色。
对于某些边,要求在操作结束时为某一种颜色。
给定每条边的初始颜色,求最小操作数,以及满足操作数最小时,最小的操作路径长度和。
输入格式
第一行,一个正整数 n。
接下来 n-1 行,每行四个整数 a, b, c, d:
• 树中有一条边连接 a 和 b。
• c = 0, 1 表示初始颜色为白色、黑色。
• d = 0, 1, 2 表示最终要求为白色、要求为黑色、没有要求。
输出格式
输出一行,两个整数,表示最小操作数、操作数最小时的最小路径长度和。
样例
样例输入1
5
2 1 1 0
1 3 0 1
2 4 1 2
5 2 1 1
样例输出1
1 2
样例解释1
操作路径 {2, 1, 3}。
样例输入2
3
1 3 1 2
2 1 0 0
样例输出2
0 0
样例输入3
6
1 3 0 1
1 2 0 2
2 4 1 0
4 5 1 0
5 6 0 2
样例输出3
1 4
数据范围与提示
对于100%的数据,保证给出的图为一棵树,有 n ∈ [1,1e5 ],a, b ∈ [1, n],c ∈ {0, 1},d ∈ {0, 1, 2}。
题解
d = 2 输0,得了1分。
对于第三个数据,是一个链,我们选择贪心,每个连续的需要翻转的搞一下,
如果两个连续的块中间夹着一堆可翻可不翻的合并一下即可。
很显然 我们每个边肯定只会翻转一次,那么每个边只有翻与不翻两种情况,这为我们dp提供了条件,翻过的边我们构成了一个边集,那么反转次数的答案这个集合就是奇数度的点个数/2。
我们令(dp[u][1/0])表示点u的儿子,是否翻转它到父亲结点这个边,得到的奇数度的点的个数最小值,以及边的个数,即路径长度和。
令(w_1)表示u的儿子v,它转移的集合中有一条翻转到u的最小代价
令(w_2)则表示没有这样的翻转边的最小代价
易得
(w_1 = min(w_1 + dp[v][0],w_2 + dp[v][1]))
(w_2 = min(w_1 + dp[v][1],w_2 + dp[v][0]))
初始化(w_1)为极大值,(w_2)清零
如果u的头顶必须要翻转
(dp[u][0] = inf)
(dp[u][1] = min(mp(w_1.first,w_1.second + 1),mp(w_2.first + 1,w_2.second + 1)))
分别代表u翻转向上延伸,奇数点不变,路径长度+1 和u独立翻转出去,奇数点与路径长均+1
如果u的头顶一定不需要翻转
(dp[u][1] = inf)
(dp[u][0] = min(mp(w_1.first + 1,w_1.second),w_2))
表示u成为奇数点,或没有操作
不是很好描述(原谅我的口胡)
放代码吧
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<ctime>
#define mp(a,b) make_pair(a,b)
#define fi first
#define pii pair<int,int>
#define se second
#define LL long long
#define N 100005
#define inf 0x3f3f3f3f
using namespace std;
int n;
pii dp[N][2];
vector<pii>G[N];
pii operator + (pii a,pii b){
return mp(a.fi + b.fi,a.se + b.se);
}
void dfs(int u,int fa,int op){
pii w1,w2,rw1,rw2;
w1.fi = w1.se = inf;
w2.fi = w2.se = 0;
for (int i = 0;i < G[u].size();i ++){
int v = G[u][i].fi,flag = G[u][i].se;
if (v == fa)
continue;
dfs(v,u,flag);
rw1 = min(w1 + dp[v][0],w2 + dp[v][1]);
rw2 = min(w1 + dp[v][1],w2 + dp[v][0]);
w1 = rw1,w2 = rw2;
}
if (op == 1 || op == 2)
dp[u][1] = min(mp(w1.fi,w1.se + 1),mp(w2.fi + 1,w2.se + 1));
else
dp[u][1] = mp(inf,inf);
if (op == 0 || op == 2)
dp[u][0] = min(w2,mp(w1.fi + 1,w1.se));
else
dp[u][0] = mp(inf,inf);
}
int main(){
scanf ("%d",&n);
for (int i = 1;i < n;i ++){
int a,b,c,d,op;
scanf ("%d%d%d%d",&a,&b,&c,&d);
if (d == 2)
op = 2;
else if (c != d)
op = 1;
else
op = 0;
G[a].push_back(mp(b,op));
G[b].push_back(mp(a,op));
}
dfs(1,0,2);
printf("%d %d
",dp[1][0].fi / 2,dp[1][0].se);
}