POJ 3585 Accumulation Degree【换根DP】

传送门http://poj.org/problem?id=3585

题意:给定一张无根图,给定每条边的容量,随便取一点使得从这个点出发作为源点,发出的流量最大,并且输出这个最大的流量。 

思路:最近开始做树形DP这部分的题,发现存图部分不是太会,这道题需要用到邻接表存图,不熟悉的朋友可以见我的另一篇博客:https://blog.csdn.net/weixin_43820920/article/details/98610704

言归正传,这道题算是换根DP的裸题了,就是先随便找一个点做为根结点(u),从这个根结点(u)出发,依次找到其叶子结点

的父亲结点的最大流量,然后在依次从下往上更新,即可得到,该根结点(u)的最大流量。但是题意并没有给我们指定结点,所

以这就需要我们的换根了。我们可以依次枚举该根结点的孩子结点(v),将它的孩子节点(v)假设为符合题意的结点,依次更新。

d[x]为以x为根的子树中,把x作为源点,从x发出的流量的最大值,这个用dfs扫描一次即可得出。 状态转移方程:

d[x]=sum deg[y]==1?v(x,y):min(d[y],v(x,y))(yin son(x))

f[x]为以x作为整棵树的根得出的答案,也只要dfs再次扫描一次。 第一次dfs是以u为根扫描的所以即是f[root]=d[root]

状态转移方程:

f[y]=d[y]+(deg[x]==1?v(x,y):min(f[x]-min(d[y],v(x,y)),v(x,y))),y in son(x)

这个dfs是从上往下更新。更新结点y时,由于其父节点x已知(并且假设其父结点不为叶节点),先假设f[x]去除y这条子树时为

w(w=f[x]-min(d[y]-v(x,y))),则此时的f[y]=d[y]+min(v(x,y),w),即是上面的状态转移方程

代码:

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
const int maxn = 200005;
int n;
int tot;
struct node
{
    int to;
    int next;
    int w;
};
node edges[maxn << 1];
int deg[maxn];//结点的深度
int head[maxn];
//以x为根的子树中,把x作为源点,从x发出的流量的最大值
int d[maxn];
//以x作为整棵树的根得出的答案
int f[maxn];
//邻接表存图
void add_edges(int u, int v, int w)
{
    edges[++tot].to = v;
    edges[tot].w = w;
    edges[tot].next = head[u];
    head[u] = tot;
}
void init()
{
    memset(deg, 0, sizeof(deg));
    memset(head, 0, sizeof(head));
    memset(f, 0, sizeof(f));
    memset(d, 0, sizeof(d));
    tot = 0;
}
void dfs1(int root, int fa)
{
    d[root] = 0;
    for(int i = head[root]; i; i = edges[i].next)
    {
        int v = edges[i].to;
        if(v == fa)
            continue;
        else
        {
            dfs1(v, root);
            if(deg[v] == 1)
                d[root] += edges[i].w;
            else
                d[root] += min(d[v], edges[i].w);
        }
    }
}
void dfs2(int root, int fa)
{
    for(int i = head[root]; i; i = edges[i].next)
    {
        int v = edges[i].to;
        if(v == fa)
            continue;
        else
        {
            if(deg[root] == 1)
            {
                f[v] = d[v] + edges[i].w;
            }
            else
            {
                f[v] = d[v] + min(f[root] - min(edges[i].w, d[v]), edges[i].w);
            }
            dfs2(v, root);
        }
    }
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n;
        scanf("%d", &n);
        init();
        for(int i = 1; i < n; i++)
        {
            int x, y, v;
            scanf("%d%d%d", &x, &y, &v);
            add_edges(x, y, v);
            add_edges(y, x, v);
            deg[x]++;
            deg[y]++;
        }

        if(n == 0 || n == 1)
        {
            cout << 1 << endl;
            continue;
        }
        else
        {
            int root = 1;
            dfs1(root, 0);//随便取一个点做为根结点
            f[root] = d[root];
            dfs2(root, 0);
            int ans = 0;
            for(int i = 1; i <= n; i++)
                ans = max(ans, f[i]);
            cout << ans << endl;
        }
    }
}
原文地址:https://www.cnblogs.com/yyaoling/p/12260422.html