Hdu-5915 The Fastest Runner Ms. Zhang(环套树DP)

185 篇文章 0 订阅
116 篇文章 0 订阅
Problem Description
Ms. Zhang is a journalist from Hong Kong, and she runs very very fast, so every one calls her “The fastest runner”. There are n cities (numbered from 1 to n) and n roads in the country, the roads are undirected. It costs 1 unit of time to go through each road. There exists at least one path between any pair of cities. Now Ms. Zhang can start her interview in city S, visit all of the n cities and finish her interview in city T. Since Ms. Zhang’s interview is simple and naive, the interview does not cost any time. Can you help Ms. Zhang to find the optimal S and T that minimise the total traveling time of Ms. Zhang? If there are multiple optimal solutions, output the solution with the smallest S. If there are still multiple solutions, output the solution with the smallest T.
 

Input
The first line contains only one integer T ( T40 ), which indicates the number of test cases.

Each test case contains n + 1 lines.

The first line contains one integer 1n105 .

In the next n lines, each line contains two integers x and y indicating there is a road between city x and city y.

It is guarenteed that there does not exist a road connecting one city to itself or two roads connecting the same pair of cities.
 

Output
For each test case, output one line “Case #x: t S T ”, where x is the case number(starting from 1),t is the minimal total traveling time, S and T are the index of starting and ending city.
 

Sample Input
  
  
1 10 7 1 5 3 4 5 8 9 6 4 8 6 2 7 10 3 4 1 9 7
 

Sample Output
  
  
Case #1: 10 1 10


题意:给定一个环套树的图,让找一个起点和终点,使得从起点开始每个点访问一次到终点的最小路程最少,问最小路程是多少。



分析:找到环后分两种情况,起点终点在一棵子树中,那么答案为2*n-circle-dis(s,t),或者起点终点在不同子树中,那么答案为2*n-dis(U,V)-2-dis(U,u)-dis(V,v),前者树形DP时就能统计出,后者需要对环单独扫两遍求解。


#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#define INF 0x3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define N 100005
using namespace std;
int T,n,time,u,v,jud[N],vis[N],Mans[N][2],cho[N][2],MAXVAL;
vector<int> G[N],a;
stack <int> S;
struct point
{
	int u,v;
	point() {}
	point(int x,int y)
	{
		if(x > y) swap(x,y);
		u = x;
		v = y;
	}
	friend bool operator < (point a,point b)
	{
		if(a.u == b.u) return a.v < b.v;
		return a.u < b.u; 
	} 
	void write()
	{
		printf("Case #%d: %d %d %d\n",++time,MAXVAL,u,v);
	}
}Point;
void Up_date(int u,int v)
{
	if((Mans[v][1] + 1 > Mans[u][0]) || (((Mans[v][1] + 1) == Mans[u][0]) && (cho[v][1] < cho[u][0])))
	{
		Mans[u][0] = Mans[v][1] + 1;
		cho[u][0] = cho[v][1]; 
	}
	if(Mans[u][0] > Mans[u][1] || ((Mans[u][0] == Mans[u][1]) && (cho[u][0] < cho[u][1])))
	{
		swap(Mans[u][0],Mans[u][1]);
		swap(cho[u][0],cho[u][1]);
	}
}
void dfs(int u,int fa)
{
	cho[u][0] = cho[u][1] = u;
	for(int v : G[u])
	 if(v != fa && !jud[v])
	 {
	 	dfs(v,u);
	 	Up_date(u,v);	
	 } 
	if((2*n - a.size() - (Mans[u][0] + Mans[u][1]) < MAXVAL) || ((2*n - a.size() - (Mans[u][0] + Mans[u][1]) == MAXVAL) && point(cho[u][1],cho[u][0]) < Point))
	{
		MAXVAL = 2*n - a.size() - (Mans[u][0] + Mans[u][1]);
		Point =  point(cho[u][1],cho[u][0]); 
	}
}
bool Circle(int u,int fa)
{
	S.push(u); 
	vis[u] = true;
	for(int v : G[u]) 
	 if(v != fa)
	  if(!vis[v]) 
	  {
	  	 if(Circle(v,u)) return true;
	  }
	  else 
	  {
	  	 while(S.top() != v)
	  	 {
	  	 	a.push_back(S.top());
	  	 	S.pop();
		 }
		 a.push_back(S.top());
		 S.pop();
		 return true;
	  } 
	S.pop();
	return false;
}
void deal_circle(int op)
{
	int temp_val = (-2)*N,num = 0,temp_num = 0;
	for(int u : a)
	{
		if(num++)
		{
			int got_val = 2*n - 2 - (Mans[u][1] + -1*num*op + temp_val + a.size()*(op == 1 ? 1 : 0));
			if(got_val < MAXVAL || ((got_val == MAXVAL) && point(temp_num,cho[u][1]) < Point))
			{
				MAXVAL = got_val;
				Point = point(temp_num,cho[u][1]);
			}
		}
		if(temp_val < Mans[u][1] + num*op || (temp_val == Mans[u][1] + num*op && temp_num > cho[u][1]))
		{
			temp_val = Mans[u][1] + num*op;
			temp_num = cho[u][1];
		} 
	}
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		a.clear();
		memset(jud,0,sizeof(jud));
		memset(vis,0,sizeof(vis));
		memset(Mans,0,sizeof(Mans));
		MAXVAL = N*2;
		scanf("%d",&n);
		for(int i = 1;i <= n;i++) G[i].clear();
		for(int i = 1;i <= n;i++)
		{
			scanf("%d %d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		while(!S.empty()) S.pop();
 		Circle(1,-1);
		for(int u : a) jud[u] = true;
		for(int u : a) dfs(u,-1);
		deal_circle(-1);
		deal_circle(1);
		Point.write(); 
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值