树形dp专栏
Posted baseai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树形dp专栏相关的知识,希望对你有一定的参考价值。
前言
自己树形dp太菜了,要重点搞
219D Choosing Capital for Treeland
终于自己做了一道不算那么毒瘤的换根dp
令 (f[u]) 表示以 (u) 为根,子树内总共需要交换的边数, (up[u]) 表示以 (u) 为根,子树外总共需要交换的边数。
Dfs1 求出 (f[u]) ,就有:
[f[u]=sum_{v∈son[u]} f[v] + (edge[u->v] == 1)]
edge[u->v] 表示 u->v 这条边的方向是不是 u->v
Dfs2 求出 (up[v])(注意,是从u点求u的儿子点v),容斥一下,就有:
[up[v]=f[u]-f[v]+up[u]+(+1 / -1)]
(+1 / -1) 是看 edge[u->v]是否等于 1,是的话就有多一条边交换方向,不是的话就要-1,因为多算了一条边
Code
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
inline int read() {
int x=0,f=1; char ch=getchar();
while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
return x * f;
}
const int N = 2e5+7;
int n,cnt;
int head[N],f[N],up[N];
struct Edge {
int next,to,flag;
}edge[N<<1];
inline void add(int u,int v,int flag) {
edge[++cnt] = (Edge)<%head[u],v,flag%>;
head[u] = cnt;
}
void Dfs1(int u,int fa) {
for(int i=head[u];i;i=edge[i].next) {
int v = edge[i].to;
if(v != fa) {
Dfs1(v,u);
f[u] += f[v] + (edge[i].flag==0); //·′±?
}
}
}
void Dfs2(int u,int fa) {
for(int i=head[u];i;i=edge[i].next) {
int v = edge[i].to;
if(v != fa) {
up[v] = f[u] - f[v] + up[u];
if(edge[i].flag == 1) up[v]++;
else up[v]--;
Dfs2(v,u);
}
}
}
int main()
{
n = read();
for(int i=1,u,v;i<=n-1;++i) {
u = read(), v = read();
add(u,v,1), add(v,u,0);
}
Dfs1(1,0);
Dfs2(1,0);
int ans = INF;
for(int i=1;i<=n;++i)
ans = min(ans,f[i]+up[i]);
printf("%d
",ans);
for(int i=1;i<=n;++i)
if(f[i]+up[i] == ans) printf("%d ",i);
return 0;
}
/*
4
1 4
2 4
3 4
2
1 2 3
*/
以上是关于树形dp专栏的主要内容,如果未能解决你的问题,请参考以下文章
动态规划_计数类dp_数位统计dp_状态压缩dp_树形dp_记忆化搜索
Starship Troopers(HDU 1011 树形DP)