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

题目链接:

http://codeforces.com/contest/219/problem/D

题意:

给一个n节点的有向无环图,要找一个这样的点:该点到其它n-1要逆转的道路最少,(边<u,v>,如果v要到u去,则要逆转该边方向)如果有多个这样的点,则升序输出所有

思路:

看了三篇博客,挺好的

http://blog.csdn.net/chl_3205/article/details/9284747

http://m.blog.csdn.net/qq_32570675/article/details/53691814  一遍dfs

http://blog.csdn.net/angon823/article/details/52316220

正向边权值为0,反向为1.

第一次dfs记录每个点到所有子树中需要改变的边的条数。 (自下向上推)(优化下只需求出根节点到所有的点需要改变的边的条数)

第二次dfs由父节点求子节点到所有点的需要改变的边的条数。(自上向下)

把边的方向化为权值,正向为1,逆向为0。

问题转化为找哪些点的在遍历全图后总权值最大。

这就是树形DP了,考虑每个节点,它可以从子树收获价值,也可以从父亲收获。所以dfs两遍,一边把子树的价值存到dps[i]里,再一遍把父亲的价值存到dpf[i]里。ans[i] = dps[i] + dpf[i]。这都是老套路了!

对于阴影那个点,第一遍dfs求出所有以下子节点(子树)对他的贡献,那么只有那根红线没有计算,第二遍dfs是计算父亲对他的贡献

代码:

代码一:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll 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*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 2e5+10;

int n,res,dp[maxn];
bool vis[maxn];
vector<pair<int,int> > g[maxn];

void dfs1(int u){
    vis[u] = 1;
    for(int i=0; i<(int)g[u].size(); i++){
        int v = g[u][i].first;
        if(vis[v]) continue;
        dfs1(v);
        res += g[u][i].second;
    }
}

void dfs2(int u){
    vis[u] = 1;
    for(int i=0; i<(int)g[u].size(); i++){
        int v = g[u][i].first, w = g[u][i].second;
        if(vis[v]) continue;
        if(w) dp[v] = dp[u]-1;
        else dp[v] = dp[u]+1;
        dfs2(v);
    }
}

int main(){
    cin >> n;
    for(int i=1; i<n; i++){
        int u,v; scanf("%d%d",&u,&v);
        g[u].push_back(MP(v,0));
        g[v].push_back(MP(u,1));
    }

    dfs1(1);
    dp[1] = res;
    MS(vis);
    dfs2(1);

    int mi = INF, last;
    for(int i=1; i<=n; i++)
        if(dp[i] <= mi)
            mi = dp[i];

    cout << mi << endl;
    for(int i=1; i<=n; i++){
        if(dp[i] == mi){
            cout << i << " ";
        }
    }
    puts("");

    return 0;
}

代码二:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll 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*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e6+10;

int n,dps[maxn],dpf[maxn];
vector<pair<int,int> > g[maxn];

void dfs1(int u,int fa){
    for(int i=0; i<(int)g[u].size(); i++){
        int v = g[u][i].first, w = g[u][i].second;
        if(v == fa) continue;
        dfs1(v,u);
        dps[u] += w;
        dps[u] += dps[v];
    }
}

void dfs2(int u,int fa){
    for(int i=0; i<(int)g[u].size(); i++){
        int v = g[u][i].first, w = g[u][i].second;
        if(v == fa) continue;
        dpf[v] = dpf[u]+dps[u]-dps[v]-w + (w?0:1);
        dfs2(v,u);
    }
}

int main(){
    cin >> n;
    for(int i=1; i<n; i++){
        int u,v; scanf("%d%d",&u,&v);
        g[u].push_back(MP(v,0));
        g[v].push_back(MP(u,1));
    }

    dfs1(1,-1);
    dfs2(1,-1);

    int mi = INF;
    for(int i=1; i<=n; i++)
        mi = min(mi,dps[i]+dpf[i]);
    cout << mi << endl;
    for(int i=1; i<=n; i++)
        if(dps[i]+dpf[i] == mi)
            cout << i << " ";
    puts("");

    return 0;
}
时间: 2024-10-27 08:30:44

CodeForces 219D.Choosing Capital for Treeland (树形dp)的相关文章

CF 219D Choosing Capital for Treeland 树形DP 好题

一个国家,有n座城市,编号为1~n,有n-1条有向边 如果不考虑边的有向性,这n个城市刚好构成一棵树 现在国王要在这n个城市中选择一个作为首都 要求:从首都可以到达这个国家的任何一个城市(边是有向的) 所以一个城市作为首都,可能会有若干边需要改变方向 现在问,选择哪些城市作为首都,需要改变方向的边最少. 输出最少需要改变方向的边数 输出可以作为首都的编号 树形DP 先假定城市1作为首都 令tree(i)表示以i为根的子树 dp[i]表示在tree(i)中,若以i为首都的话,需要改变的边数 第一次

Codeforces 219D. Choosing Capital for Treeland (树dp)

题目链接:http://codeforces.com/contest/219/problem/D 树dp 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio&

(纪念第一道完全自己想的树DP)CodeForces 219D Choosing Capital for Treeland

Choosing Capital for Treeland time limit per test 3 seconds memory limit per test 256 megabytes input standard input output standard output The country Treeland consists of n cities, some pairs of them are connected with unidirectional roads. Overall

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

题目连接: codeforces 219D 题目大意: 给出一棵树,但是它的边是有向边,选择一个城市,问最少调整多少条边的方向能使一个选中城市可以到达所有的点,输出最小的调整的边数,和对应的点. 题目分析: 定义dp[u]为以u为根的子树中要使根都可达,需要调换方向的边的条数. 定义dir[v]记录点v到父亲节点的边的方向. 然后就是将每个点提成根的操作了. dp[u]换成新的定义,以u为根的到达整棵树需要调整的边的条数. dp[u] = dp[p] + dir[u]?1:-1 详情见代码,这道

Codeforces 219D Choosing Capital for Treeland(树形DP)

题目是给一张边有向的树形图.要选出首都的点,首都要都能走到其他点,因此要反转一些边的方向.问可以选哪几个点作为首都,使它们所需反转边的数量最少. 这题挺好想的,因为做过HDU2196. 首先就不妨设正向边权值为0,反向边权值为1,那样就是各个点出发到其他点经过边所需的最少权值和. 然后对于每个点,分两个部分考虑:以这个点为根的子树.这个点往上走的部分: dp[0][u]表示以u点作为首都且以u点为根的子树部分所需反转边的数量,容易知道就等于子树内边权和 dp[1][u]表示以u点作为首都且u点向

CodeForces 219D Choosing Capital for Treeland (树形DP)

题意:给一个树形图,n个节点,n-1条有向边,要求选一个节点作为根,使需要改变方向的边的数目最少.并输出所有可能作为根的点. 思路: 先随便一个点进行DFS,计算将每棵子树的边全部往下时,所需要的费用down[i].还是那个点进行DFS,这次就要求答案了,尝试将每个点t作为根,那么以t作为根的总费用=down[t]+父亲这棵子树.down[t]已经在第一次DFS中求出,而父亲这棵子树就不是down[父亲]了,而是down[父亲]-down[t]+w(父亲,t).注:w为边权. 1 #includ

Codeforces 219D Choosing Capital for Treeland 2次DP

//选择一个根使得变换最少边的方向使得能够到达所有点#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #i

codeforces:219D. Choosing Capital for Treeland

题目大意:国家由n个城市以及n-1条连接不同城市的道路组成(每条道路都有正向和逆向之分),并且每个城市到另外一个城市都至少存在一条路径.现在议会要决定选一个城市作为首都.当一个城市选为首都时,需要将所有从首都到其它城市的路径上的所有边都是正向的(如果不是正向的则需要颠转道路).求这样的首都,使得需要颠转的道路数目最小. 其中2<=n<=2e5. 首先这显然是一副无向无环连通图(参考我的博客连通图的一些性质).因此从任意一个城市出发到另外一个城市都有唯一一条路径. 为了后面分析的简便,这里记选取

树形DP Codeforces Round #135 (Div. 2) D. Choosing Capital for Treeland

题目传送门 1 /* 2 题意:求一个点为根节点,使得到其他所有点的距离最短,是有向边,反向的距离+1 3 树形DP:首先假设1为根节点,自下而上计算dp[1](根节点到其他点的距离),然后再从1开始,自上而下计算dp[v], 4 此时可以从上个节点的信息递推出来 5 */ 6 #include <cstdio> 7 #include <cstring> 8 #include <cmath> 9 #include <vector> 10 using name