Codeforces 239E World Eater Brothers【思维+树形DP】

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 and bi (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.

Examples
Input
4
2 1
3 1
4 1

Output
1

Input
5
2 1
2 3
4 3
4 5

Output
0

dp[i]记录以i为起点,反向边数量.
mx 记录以i为起点,反向边-正向边的最大数
如果mx>0 说明某个点到i中反边大于正边,最优就是以这个点为起点,那么正向边就变为反向边,反向边变为正向边,以i为起点的最小花费为dp[i]-mx

枚举每一条边,相当于切割每一条边,求这条边两个端点分别为起点的最小花费,最后将花费加起来,求最小的花费

#include<bits/stdc++.h>
using namespace std;
#define maxn 100010
struct Edge{
	int from,to,nxt,w;
}edge[maxn<<2];
int tot=0,tmp,ans,mx,dp[maxn],head[maxn];
void add(int u,int v,int w){
	edge[++tot].from=u; edge[tot].to=v; edge[tot].nxt=head[u]; edge[tot].w=w; head[u]=tot;
}
void dfs(int u,int pre,int val){
	dp[u]=0;
	for(int i=head[u];~i;i=edge[i].nxt){
		int to=edge[i].to;
		if(to==pre) continue;
		dfs(to,u,val-edge[i].w);
		dp[u]+=dp[to]+(edge[i].w!=1);
	}
	mx=max(mx,val);
}
int main(){
	memset(head,-1,sizeof(head));
	int n; scanf("%d",&n);
	if(n==1){
		cout<<"0"<<endl; return 0;
	}
	for(int i=1;i<n;i++){
		int u,v; scanf("%d%d",&u,&v);
		add(u,v,1); add(v,u,-1);
	}
	ans=(1<<30);
	for(int i=1;i<=tot;i+=2){
		int u=edge[i].from; int v=edge[i].to; tmp=0;
		mx=-(1<<30); dfs(u,v,0); tmp+=dp[u]-mx;
		mx=-(1<<30); dfs(v,u,0); tmp+=dp[v]-mx;
		ans=min(ans,tmp);
	}
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值