吝啬的国度

时间限制:1000 ms  |  内存限制:65535

难度:3

描述

在一个吝啬的国度里有N个城市,这N个城市间只有N-1条路把这个N个城市连接起来。现在,Tom在第S号城市,他有张该国地图,他想知道如果自己要去参观第T号城市,必须经过的前一个城市是几号城市(假设你不走重复的路)。

输入

第一行输入一个整数M表示测试数据共有M(1<=M<=5)组
每组测试数据的第一行输入一个正整数N(1<=N<=100000)和一个正整数S(1<=S<=100000),N表示城市的总个数,S表示参观者所在城市的编号
随后的N-1行,每行有两个正整数a,b(1<=a,b<=N),表示第a号城市和第b号城市之间有一条路连通。

输出

每组测试数据输N个正整数,其中,第i个数表示从S走到i号城市,必须要经过的上一个城市的编号。(其中i=S时,请输出-1)

样例输入

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

样例输出

-1 1 10 10 9 8 3 1 1 8

来源

​经典题目​

 

 

 

 

纯吐槽:自己写的总是超时,然后就搜大神的代码。但是发现每一篇代码都一样。要么是vector数组,解决二维数组太大超内存的问题。要么直接是一维数组直接在输入的时候建成有向图。题目解释还都一样,真是搞不懂啊!

这里我就通过别人的解释再用用多组数据测试记录一下对这个问题的理解过程。

 

看别人的代码学到的东西,使用vector数组解决二维数据超内存问题。

              因为给定的城市N的数目太大,建立数组需要用到#include<vector>,vector就是一个不定长数组,vector<int>a就是一个类似于int a[]的整            数数组,只不过他的长度不确定,可以用a.size()读取他的长度。

 

                而vector<int>a[max]就是一个二维数组,只是第一维的大小是固定的(不超过max),二维的大小就不固定了,这道题之所以用到vector            就是利用了他的不定长,如果直接建立二维数组a[n][n],n太大了,这样的二维数组绝对超出内存。

(1)头文件#include<vector>.

      (2)创建vector对象,vector<int> vec;

      (3)尾部插入数字:vec.push_back(a);

      (4)使用下标访问元素,cout<<vec[0]<<endl;记住下标是从0开始的。

      (5)向量大小:vec.size();

 

 

 

 

剖析题意,从输入数据中可以得到各个城市之间的无向图(因为双向),而我们需要得到的是一个以s城市为起点的有向图。

我们用mp[i]来表示从s到i号城市必须经过的上一个城市编号。

为了更形象的描述,我们在这里 将 mp[a]=b  称为 b指向a  显然,每个节点智能被一个城市指向(否则无法保存嘛)

初始化 mp[i]=0;

则对于每组边的两节点 a,b  

     若mp[b]=0,即无城市指向b,则令a指向b:mp[b]=a

     若mp[b]!=0, 即已经有城市指向b,那么,这时当然要将b指向a,即mp[a]=b;

          然而,我们并不知道是否有城市指向a,若有 则mp[a]!=0 ,上述操作会将此值覆盖

          所以,在此之前,我们需要将所有指向a的城市节点,方向调换,即若t指向a,则改为a指向t

          同时,再以t为起始递归下去,这样就可以让已a为起点的一系列有向线路全部转换方向,实现180转弯。

     经过上述操作我们已经得到一个有向图,但仍然不能满足题目要求,因为起点是s,故每个有向线路的起点必然是由s开始的,

      所以,我们对s点,进行上述的递归操作,此时,任务完成,所有的有向路都是已s点为出发点的了。

 

读了这个解释发现题目给出的数据根本不需要任何变化是吧!搜索函数也没有用上是吧!

那么让我们来举出几组简单的数据测试一下吧!

1
11   1
1     9
1     8
8    10
10   3
8     6
1     2
10   4
9     5
3     7
11   4

发现了吧!这组数据我加入了   11   4 这条路,那么你就会发现10  4  有一条路了。我们的目的是建一条有向路是吧,有向路什么么啊!是不是每个节点的入度必须是1吧!出度是大于等于1吧。我对有向图的理解暂且是这样的(还在不断的学习中)。输入   11   4的时候map[4]=10  说明4已经有爹了怎么办。那么11就不能再当4的爹了吧!如果一个小伙两个爹,世界不就乱套了吗!怎么办,不要怕11只能当孩子了,人家4都长大了也该有孩子了,那就让11叫4爹吧!这也不行啊!你说让人家叫就叫啊!如果11要是个私生子,那人家不就也有爹了吗?对额!那么好的dfs函数终于第一次用上了,看代码B处是吧dfs(a)孙悟空的火眼金睛来了,要看一看11是不是有个不为人知的爹呢!dfs()函数就是火眼金睛啊!一看哇塞map[11]=0  立马发现11没爹就是个石猴啊!来吧就让唐僧(4)收了吧!直接map[11]=4磕头叫爹。

那问题来了,这火眼金睛我也是没搞懂啊

 

if(pre){
dfs(pre); // A
map[pre]=x;
}

 

 

 

这是干嘛的啊!火眼金睛怎能让你们这些小妖看懂那还了得。不过看你这小妖挺勤奋还不坏就偷偷的告诉你吧!悟空天生聪明而且还细心,看到11是个私生子了还没有声张而是偷偷的给11的私生爹做了标记pre就是他爹的名字。哎!有些人只图一时快活随处播种,聪明的悟空是非常了解这种人的,所以悟空怀疑pre也是个私生子,怎么办这也不能直接问人家啊!伤自尊啊!那么就使用必杀技(火眼金睛)吧(dfs(pre))!一看哇塞pre竟然也是个石猴啊!那就让pre给4叫爹吧(这里我要严肃的说一下深搜了dfs,意思是dfs一下如果pre有爹了,不执行map[pre]=x,接着再dfs直到找不到爹再执行map[pre]=x,如果pre没爹了就让map[pre]=x,让pre给x叫爹)

 

 

 

有点复杂啊!捋一捋思路

10   4

11   4

4有爹了不能让11当爹了。

那11有爹吗dfs一下找到11的祖宗是谁。

让11的祖宗管4叫爹就行了(因为11    4是上下辈的关系必须有一个是爹)。

看过家谱的都懂每个人只有一个爹。

 

 

1
11    1
1      9
1      8
8     10
10    3
8      6
2     11
10    4
9       5
3      7
11    4

再来看一个家谱

   2    11

  10    4

   11   4       

怎么办人家11也有爹了,但是2没爹啊,让2 给4叫爹这就不会乱套了

就这样续家谱,自己品味吧!

 

 

 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int map[100001];
void dfs(int x){
int pre=map[x];
if(pre){
dfs(pre); // A
map[pre]=x;
}
}
int main()
{
int i,n,num,s,a,b;
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&num,&s);
memset(map,0,sizeof(map));
for(i=1;i<num;i++)
{
cin>>a>>b;
if(!map[b])//如果入度是0就记录那么要不是0就要转到C除操作了吧!
map[b]=a;//这里是记录了指向b的a吧 a-->b
else{ // C
dfs(a); // B 火眼金睛看穿一切
map[a]=b;<span style="white-space:pre"> </span>// 别墨迹了磕头叫爹吧!
}
}
adjust(s);
map[s]=-1;
for(i=1;i<num;i++)
printf("%d ",map[i]);
printf("%d\n",map[i]);
}
return 0;
}