codeforces 219D D. Choosing Capital for Treeland(树形dp)

本文探讨了一种算法,旨在解决在给定的有向树形结构中,通过最少调整边的方向,使得指定节点能够到达树中所有其他节点的问题。详细介绍了状态转移方程和递归解决方案,并提供了AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目连接:

codeforces 219D


题目大意:

给出一棵树,但是它的边是有向边,选择一个城市,问最少调整多少条边的方向能使一个选中城市可以到达所有的点,输出最小的调整的边数,和对应的点。


题目分析:

  • 定义dp[u]为以u为根的子树中要使根都可达,需要调换方向的边的条数。
  • 定义dir[v]记录点v到父亲节点的边的方向。
  • 然后就是将每个点提成根的操作了。
  • dp[u]换成新的定义,以u为根的到达整棵树需要调整的边的条数。
  • dp[u] = dp[p] + dir[u]?1:-1
  • 详情见代码,这道题在D题当中算相当水的了。

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define MAX 200007

using namespace std;

int n,s,t;
vector<int> e[MAX];
vector<int> f[MAX];
int dp[MAX],dir[MAX];

void add ( int u , int v )
{
    e[u].push_back ( v );
    f[u].push_back ( 0 );
    e[v].push_back ( u );
    f[v].push_back ( 1 );
}

void dfs ( int u , int p )
{
    dp[u] = 0;
    for ( int i = 0 ; i < e[u].size() ; i++ )
    {
        int v = e[u][i];
        if ( v == p ) continue;
        dir[v] = f[u][i];
        dfs ( v , u );
        dp[u] += dp[v] + dir[v];
    }
}

void solve ( int u , int p )
{
    for ( int i = 0 ; i < e[u].size() ; i++ )
    {
        int v = e[u][i];
        if ( v == p ) continue;
        dp[v] = dp[u] + (dir[v]?-1:1);
        solve ( v , u );
    }
}


int main ( )
{
    while ( ~scanf ( "%d" , &n ))
    {
        for ( int i = 0 ; i < MAX ; i++ )
        {
            e[i].clear();
            f[i].clear();
        }
        for ( int i = 1 ; i < n ; i++ )
        {
            scanf ( "%d%d" , &s , &t );
            add ( s , t );
        }
        dfs ( 1 , -1 );
        solve ( 1 , -1 );
        int ans = 1<<29;
        for ( int i = 1 ; i <= n ; i++ )
            ans = min ( ans , dp[i] );
        printf ( "%d\n" , ans );
        vector<int> pp;
        for ( int i = 1 ; i <= n ; i++ )
            if ( dp[i] == ans )
                pp.push_back ( i );
        sort ( pp.begin() , pp.end());
        for ( int i = 0 ; i < pp.size(); i++ )
            printf ( "%d " , pp[i] );
        puts ("");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值