金山西山居2013程序挑战赛初赛 第三题

 

Problem Description

 

  在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录。
  这里我们简化一下问题,假设只有一个根目录,CD操作也只有两种方式:
  
  1. CD 当前目录名\...\目标目录名 (中间可以包含若干目录,保证目标目录通过绝对路径可达)
  2. CD .. (返回当前目录的上级目录)
  
  现在给出当前目录和一个目标目录,请问最少需要几次CD操作才能将当前目录变成目标目录?
 


 

Input

 

输入数据第一行包含一个整数T(T<=20),表示样例个数;
每个样例首先一行是两个整数N和M(1<=N,M<=100000),表示有N个目录和M个询问;
接下来N-1行每行两个目录名A B(目录名是只含有数字或字母,长度小于40的字符串),表示A的父目录是B。
最后M行每行两个目录名A B,表示询问将当前目录从A变成B最少要多少次CD操作。
数据保证合法,一定存在一个根目录,每个目录都能从根目录访问到。
 


 

Output

 

请输出每次询问的结果,每个查询的输出占一行。
 


 

Sample Input

 

  
  
2 3 1 B A C A B C 3 2 B A C B A C C A
 


 

Sample Output

 

  
  
2 1 2
 


类别:图论 难度:1.5

题意分析:给定一个树即每个点的父节点,求从a到b需要几步(向上遍历一次算一步,向下遍历到子树任一节点算一步)。

先用map存储<字符串,标号>,然后记录每个点父节点,再计算每个点的深度,之后对于给定的所求a点和b点,求a和b遍历到最近的共同父节点的距离,记为dx,dy,最后结果即为dx+((dy>0)?1:0)。一开始map忘记每次循环clear,导致runtime error(堆栈溢出)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<string>
using namespace std;

const int N=101010;
map<string,int> in;
int n,m,dep[N],pa[N],qu[N];
char a[200],b[200];

int main()
{
	int t;
	scanf("%d",&t);
	for(int cnt=1;cnt<=t;cnt++)
	{
		memset(dep,-1,sizeof(dep));
		memset(pa,0,sizeof(pa));
		in.clear();
		scanf("%d%d",&n,&m);
		int i,j,ct=1;
		for(i=0;i<n-1;i++)
		{
			scanf("%s%s",a,b);
			map<string,int>::iterator it;
			it = in.find(a);
			if(it==in.end()) in[a]=ct++;
			it = in.find(b);
			if(it==in.end()) in[b]=ct++;
			pa[in[a]]=in[b];
		}
		int top,tmp,kn;
		for(i=1;i<=n;i++)
			if(pa[i]==0) {dep[i]=0;break;}
		for(i=1;i<=n;i++)
		{
			if(pa[i]==0) continue;
			top=0;
			tmp=pa[i];
			while(dep[tmp]<0)
			{
				qu[top++]=tmp;
				tmp=pa[tmp];
			}
			kn=dep[tmp];
			while(top>0)
			{
				dep[qu[--top]]=++kn;
			}
			dep[i]=++kn;
		}
		for(i=0;i<m;i++)
		{
			scanf("%s%s",a,b);
			int x=in[a],y=in[b];
			int dx=0,dy=0;
			while(pa[x]>0&&dep[x]>dep[y])
			{
				x=pa[x];
				dx++;
			}
			while(pa[y]>0&&dep[y]>dep[x])
			{
				y=pa[y];
				dy++;
			}
			while(x!=y)
			{
				x=pa[x];y=pa[y];
				dx++;dy++;
			}
			printf("%d\n",dx+((dy>0)?1:0));
		}
	}
}



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值