您的位置:首页 > 其它

CodeForces 238C World Eater Brothers(tree dp)

2015-03-14 21:17 901 查看
World Eater Brothers(tree dp)

[b]Description

[/b]

You must have heard of the two brothers dreaming of ruling the world. With all their previous plans failed, this time they

decided to cooperate with each other in order to rule the world.

As you know there are n countries in the world. These countries are connected by n - 1 directed roads. If you don't consider direction
of the roads there is a unique path between every pair of countries in the world, passing through each

road at most once.

Each of the brothers wants to establish his reign in some country, then it's possible for him to control the countries that

can be reached from his country using directed roads.

The brothers can rule the world if there exists at most two countries for brothers to choose (and establish their reign in

these countries) so that any other country is under control of at least one of them. In order to make this possible they want

to change the direction of minimum number of roads. Your task is to calculate this minimum number of roads.
[b]Input

[/b]

The first line of input contains an integer n(1 ≤ n ≤ 3000).
Each of the next n - 1 lines contains two space-separated integers ai andbi(1 ≤ ai, bi ≤ n; ai ≠ bi) saying
there is a road from country ai to country bi.
Consider that countries are numbered from 1 to n. It's guaranteed that if you don't consider direction of the roads there
is
a unique path between every pair of countries in the world, passing through each road at most once.
Output
In
the only line of output print the minimum number of roads that their direction should be changed so that the brothers
will
be able to rule the world.

Sample
Input


[b]Input

[/b]

4
2 1
3 1
4 1

Output

[b][b]1[/b][/b]
[b][b]Input

[/b][/b]

5
2 1
2 3
4 3
4 5

Output

[b][b]0[/b][/b]

题意:

给定n个点的有向树,下面n-1行给出正向的边。

问:修改尽可能小的边的方向,可以任选2个起点使得这两个点bfs能遍历完所有点。

思路:枚举每一条边,把这条边断开,把树分成两部分,每次计算这两部分的最小花费。

这样我们每次只要计算一个子树上所需要改动的最小边数。

对于一棵树所需要修改的最小边方向的方法:

先求出表示以i为根的子树,用i作为起点的花费,即dp[i];

但是在这个子树中,以 i 为起点并不一定是最优解,所以我们要去寻找这个最优的起点 u ;

树种的任意起点 u 遍历整棵树所需要改变的边数不用重新去遍历每条边,只要看看它和 i 这个点之间的那段路需要改变多少边数即可。

即:cost[u] = dp[root] - ((u->root方向的边数)- (root->u方向的边数))

为了得到最小的cost[u],就必需使 ((u->root方向的边数)-(root->u方向的边数)) 最大

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 10000001
const int maxn=1e5+10;
int head[maxn];
int dp[maxn];
int n,u,v,edgenum,arr;
struct Edge
{
int from, to, dis, next;
}edge[maxn<<1];

void add(int u, int v, int d)
{
Edge E={u,v,d,head[u]};
edge[edgenum]=E;
head[u]=edgenum++;
}

void dfs(int u, int fa, int val)//val记录每个子节点回到根需要改变的边数
{
dp[u]=0;
for(int i=head[u]; ~i; i=edge[i].next)//~i:i!=-1
{
int v=edge[i].to;
if(v==fa) continue;
dfs(v, u, val-edge[i].dis);
dp[u]+=dp[v]+(edge[i].dis!=1); //从u点出发,遍历它的子节点需要改变的根数
}
arr= max(arr, val);  // 寻找每个子节点到根节点能够少改变的边数的最大值
//就是在求边断开后 ,这个子树里从最优的那个点出发需要改变边数=dp[u]-arr;
}

int main()
{
while(scanf("%d", &n)!=EOF)
{
if(n==1)
{
puts("0"); continue;
}
memset(head,-1,sizeof(head));
edgenum=0; //记录边的数量
for(int i=1; i<n; i++)
{
scanf("%d%d", &u, &v);
add(u,v,1);
add(v,u,-1);
}
int ans=inf;
for(int i=0; i<edgenum; i+=2)
{
u=edge[i].from;
v=edge[i].to;
arr=-inf;
dfs(u,v,0);
int tmp=dp[u]-arr;//断开后第一个子树的最优解

arr=-inf;
dfs(v,u,0);
tmp+=dp[v]-arr;//加上第二个子树的最优解
ans=min(ans, tmp); //遍历所有的边以后求得的最优解
}
printf("%d\n", ans);
}
return 0;
}


~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: