【树形dp】W

题面

【树形dp】W_第1张图片

题意

给出一棵树,每条边有初始状态。对一条路径上的边的状态进行翻转,求每条边满足结果且操作数最少,最小的操作边数之和。

思路

设f[i][0/1]表示i与fa[i]的边是否需要翻转时的答案。
枚举每个儿子,判断儿子与i的边是否处于同一个操作次数(等方案)进行转移。
【树形dp】W_第2张图片

代码

#include 
#include 
#include 
#include 

const std::pair<int, int> inf = std::make_pair(1e9, 1e9);
std::vector<std::pair<int, int> > g[200001];
std::pair<int, int> f[100001][2];
int n;

std::pair<int, int> operator+ (std::pair<int, int> a, std::pair<int, int> b) {
	return std::make_pair(a.first + b.first, a.second + b.second);
}

void dfs(int fa, int p, int type) {
	std::pair<int, int> tmp0(0, 0), tmp1(inf);
	for (int i = 0; i < g[p].size(); i++) {
		int v = g[p][i].first;
		if (v == fa) continue;
		dfs(p, v, g[p][i].second);
		std::pair<int, int> nxt0, nxt1;
		nxt0 = std::min(tmp0 + f[v][0], tmp1 + f[v][1]);
		nxt1 = std::min(tmp0 + f[v][1], tmp1 + f[v][0]);
		tmp0 = nxt0;
		tmp1 = nxt1;
	}
	if (!type || type == 2)
		f[p][0] = std::min(tmp0, std::make_pair(tmp1.first + 1, tmp1.second));
	else f[p][0] = inf;
	if (type == 1 || type == 2)
		f[p][1] = std::min(std::make_pair(tmp0.first + 1, tmp0.second + 1), std::make_pair(tmp1.first, tmp1.second + 1));
	else f[p][1] = inf;
}

int main() {
	int size = 256 << 20;
    char *p = (char*)malloc(size) + size;
    __asm__("movl %0, %%esp\n" :: "r"(p));
	scanf("%d", &n);
	for (int i = 1, a, b, c, d; i < n; i++) {
		scanf("%d %d %d %d", &a, &b, &c, &d);
		if (d != 2) d = c ^ d;
		g[a].push_back(std::make_pair(b, d));
		g[b].push_back(std::make_pair(a, d));
	}
	dfs(0, 1, 0);
	printf("%d %d", f[1][0].first / 2, f[1][0].second);
}

你可能感兴趣的:(动态规划)