CodeForces 219D Choosing Capital for Treeland | 树形dp

题意:

有n个城市,它们之间有n-1条路(路都是单向的),形成一棵树。现在可以改变一些路的方向。

让你选择一个首都,使得从首都出发到达到其它城市,所需要重建的路的数量最少。

思路:

先进行一次dfs,求出每个点到达它的所有子节点所需要重建的路的数量(此时不需要理会父节点的情况,仅对子树)。

再进行一次dfs,更新每个点作为首都时所应改变的路的实际数量(对整棵树而言),这个可以自己思考一下应该怎么更新。

*建图的时候,需要保存为双向边,要记录一下哪条边才是实际存在的边。

AC代码:

#include <cstring>  
#include <cstdlib>  
#include <iostream>  
#include <cstdio>  
#include <vector>  
using namespace std;  
const int MAXN = 2*1e5+5;  
struct Edge  
{  
        int v, next;  
        bool flag;  
};  
int dp[MAXN], n, pp;  
int head[MAXN];  
Edge edge[2*MAXN];  
bool vis[MAXN];  
int res[MAXN], minres;  
void addEdge(int u, int v, bool flag)  
{  
        edge[pp] = (Edge){v, head[u], flag};  
        head[u] = pp++;  
}  
  
void dfs(int u)  
{  
        dp[u] = 0;  
        int next = head[u];  
        vis[u] = true;  
        while(next != -1)  
        {  
                Edge& e = edge[next];  
                if(!vis[e.v])  
                {  
                        dfs(e.v);  
                        dp[u] += dp[e.v];  
                        if(e.flag == false) dp[u]++;  
                }  
                next = e.next;  
        }  
}  
void dfs2(int u)  
{  
        int next =head[u];  
        vis[u] = true;  
        while(next != -1)  
        {  
                Edge &e = edge[next];  
                if(!vis[e.v])  
                {  
                        if(e.flag == false)     res[e.v] = res[u]-1;    //res[e.v] = dp[e.v]+res[u]-dp[e.v]-1;  
                        else                    res[e.v] = res[u]+1;    //res[e.v] = dp[e.v]+res[u]-dp[e.v]+1;  
                        minres = min(minres, res[e.v]);  
                        dfs2(e.v);  
                }  
                next = e.next;  
        }  
}  
int main()  
{  
        memset(head, -1, sizeof(head));  
        scanf("%d" ,&n);  
        int u, v;  
        minres = 2*n;  
        for(int i = 0;i < n-1; i++)  
        {  
                scanf("%d%d" ,&u, &v);  
                addEdge(u, v, true);  
                addEdge(v, u, false);  
        }  
        //  
        dfs(1);  
        memset(vis, false ,sizeof(vis));  
        //  
        /* 
        cout<<"test"<<endl; 
        for(int i = 1;i <= n; i++) 
                cout<<dp[i]<<" "; 
        puts("");*/  
        res[1] = dp[1];  
        minres = min(minres, res[1]);  
        dfs2(1);  
        printf("%d\n", minres);  
        int mark = 0;  
        for(int i = 1;i <= n; i++)  
        {  
                if(res[i] == minres)  
                {  
                        if(mark == 0)   printf("%d", i), mark = 1;  
                        else  
                                printf(" %d",i);  
                }  
        }  
        puts("");  
        //  
        return 0;  
}  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值