CodeForces 238C World Eater Brothers(tree dp)

                                       World Eater Brothers(tree dp)

Description

 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.

Input

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 ≤ nai ≠ 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

Input

4
2 1
3 1
4 1
Output

1

Input

5
2 1
2 3
4 3
4 5
Output

0

题意:

给定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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值