2016微软探星夏令营在线技术笔试题解(3)

题目来源: 
        HihoCoder1343
 

题目描述:
    Recently Little Hi joined an algorithm learning group. The group consists of one algorithm master and members. The members are numbered from 1 to N. Each member has one or more other members as his mentors. Some members' mentor is the master himself.

    
    Every week each member sends a report of his own learning progress and the reports collected from his pupils (if there is any) to his mentors. The group is so well designed that there is no loop in the reporting chain so no one receives his own report from his pupil. And finally the master gets every one's report (maybe more than once).

    Little Hi notices that for some members their reporting routes to the master can be easily cut off by a single member's (other than the master and himself) absence from the reporting duty. They are called unstable members while the others are stable members. Given the reporting network of the group, can you find out how many members are stable?

    Assume there are 4 members in the group. Member 1 and 2 both have the master as their only mentor. Member 3 has 2 mentors: member 1 and member 2. Member 4 has 1 mentor: member 3. Then member 4 is the only unstable member in the group because if member 3 is absent his learning report will be unable to be sent to the master. 

题目大意:
    一个小组中有N个成员和一个Master。每个成员将制定1个或多个成员作为自己的mentor,成员也可以选择Master作为自己的mentor。每周每个成员都要将自己的报告以及收集到的所有来自于自己pupil的报告提交给自己的每一个mentor,最后Master会受到所有小组成员的报告。
    现定义这样的成员为“不稳定成员”:如果存在一个非Master的成员,当他缺席时,当前成员就无法将自己的报告传递给Master。题目要求在给定成员的信息后,求出小组中“稳定成员”的数目。
    题目给出一个样例:小组中有4个成员,1号成员和2号成员仅有一个mentor是Master;3号成员有两个mentor:1号和2号成员;4号成员有1个mentor:3号成员。此时,不稳定成员是4号,因为3号成员如果缺席,4号成员就无法将自己的报告传递给Master。

解答:
    本次笔试的后两题都是和图算法有关的,本题的难度中等,考察了图的拓扑排序,以及一种称为“支配树”的数据结构。基本解题思路是:根据给定的输入信息构建图,然后构建支配树,求解答案。

·数据存储:
    根据输入的成员信息,可以构建一个有向图来直观地表示成员之间的信息。此时Master和每一个成员分别用一个点来表示,当成员A是成员B的mentor时,点A到点B,之间就存在一条边。题目中给出的样例可以用下图来表示:

 
    这里用编号0来表示Master。此时,“不稳定成员”的定义就是:如果存在某一个编号不为0的点,使得从点0到该点的所有路径中都必须经过这个点,那么该点代表的成员就是“不稳定成员”。上图中,从点0到点4的所有路径必经过点3,因此成员4是不稳定成员。

支配树:
    将上面“不稳定成员”的定义加以拓展,去掉“非0点”的限制,可以得到“支配点”的定义,即:对于某一个目标点,如果存在一个点,使得图中从起点到目标点所有路径都必须经过该点,那么该点就是目标点的“支配点”。上图中:点1、2、3、4的支配点分别为:0、0、0、0和3。显然,如果从起点出发可以到达图中的所有点,那么起点就是图中所有点的“支配点”。
    一个点的“支配点”不一定只有一个。例如:如果对于某个点,从起点到该点的路径只有1条,那么该路径上的所有点都是该点的支配点。对于有多个支配点的情况,我们可以找到一个支配点,它距离目标点的最近,这个点我们成为“
直接支配点”。对于给定的图,一个点可能用有多个支配点,但它的直接支配点一定是唯一的。此时,出去起点外,所有的点都有自己唯一的“直接支配点”。
    而“支配树”是这样的一种多叉树:图中的点对应于树的节点,而每一个节点的父节点则是它的直接支配点。上文中的图构成的支配树如下:
 
    显然,完成树的构建后,每个点的父节点就是它的直接支配点,而这个点的所有祖先节点都是它的支配点。此时,根据题意,我们要找的“稳定成员”就是直接支配点是0号点(Master)的成员,也就是支配树中根节点的孩子。 

·建树:
    为了建立支配树,就必须知道每个点的直接支配点。考虑原图中每个点的“前驱点”,本题中即考虑每个成员的mentor。如果某个成员只有一个mentor,那么显然从Master到该成员的路径一定都会经过他的mentor,因此mentor就是该成员的直接支配点;对于抽象的图而言,如果某一个点只有一个前驱点,那么该前驱点就是当前点的直接支配点;如果某个成员有多个mentor,那么对于某一个mentor而言,从Master到该成员就未必会经过它,所以,当某个成员拥有多个mentor时,他的mentor都不是他的直接支配点;对于抽象的图而言,如果一个点有多个前驱,那么这些前驱点都不是它的直接支配点,我们需要考虑这些前驱节点的支配点,当这些前驱节点拥有共同的一个支配点时,说明从起点到这些前驱点的所有路径必会经过这个共有的支配点,也就是说,从起点到目标点的所有路径都会经过这个共有的支配点,因此这个共有的支配点就是目标点的直接支配点。这个结论对于只有一个前驱节点的情况也使用
    根据支配树的定义,多个节点共有的支配点是明确的,就是他们的公共祖先,而我们要找的则是最近公共祖先
这个结论对于只有一个前驱节点的情况也使用,因为如果只有一个点,那么它的最近公共祖先就是它自己。
    于是,建立支配树的过程就是:首先将起点加入到树中,作为整个支配树的根,然后对于每一个节点,找到其所有前驱节点在支配树中的最近公共祖先,这个祖先节点就是当前节点的父节点。

·拓扑排序:
    上面的建树过程有一个条件必须要保证,即某个点要加入到树中时,必须确保它的所有前驱点已经在树中,这样才可以找到这些点的最近公共祖先。因此,节点添加入树中的顺序是很重要的。我们可以通过拓扑排序找到合适的顺序,拓扑排序的结果就是节点加入树的顺序。这样就保证了前驱节点一定先于当前节点加入到树中。

·最近公共祖先:
    建树的过程设计到了求多个节点的最近公共祖先。我们可以采用一种复杂度为O(lgn)的算法来求解它。考虑每个节点在树中的高度,将高度小的节点沿着父节点指针向上移动,在所有节点的高度相同时再同时沿着父节点指针向上移动,当所有的节点都到达同一个节点时,这个终点就是这些节点的最近公共祖先。

    以上是本题的解答思路,完成建立支配树后,统计一下根节点有多少个孩子,就是本题的答案。

输入输出格式:
    输入:

The first line contains an integer N, the number of members.

The i-th line of the following N lines describe the mentors of the i-th member. The first integer is Ki, the number of mentors of the i-th member. Then follows Ki integers A1 ... AN, which are his mentors' numbers. Number 0 indicates that the master is one of his mentor.

For 40% of the data, 1 ≤ N ≤ 1000.

For 100% of the data, 1 ≤ N ≤ 100000.

For 100% of the data, 1 ≤ Ki ≤ 10, Ki < N, 0 ≤ Ai  N.  
    输出:
             Output the number of stable members. 

程序代码:

/****************************************************/
/* File        : HihoCoder1343                      */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-07-21                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<memory.h>

/*
 * Define the tree node.
 * Parameters:
 *		@value: The value of this node.
 *		@depth: The depth of the node in tree.
 *		@parent: The parent node.
 *		@child: The children node list.
 *		@brother: The brother nodes of this node, 
 *				it's the next field in children linklist.
 */ 
typedef struct node1 {
	int value;
	int depth;
	struct node1 *parent;
	struct node1 *child;
	struct node1 *brother;
} tree_node;

/*
 * The edge of graph.
 * Parameters:
 *		@dst: The destination node of this edge.
 *		@next: The next field in linklist.
 */
typedef struct node2 {
	int dst;
	struct node2 *next;
} edge;

/*
 * The vertex in graph, the member's message.
 * Parameters:
 *		@index: The index of this vertex.
 *		@visited: Mark if the vertex has been visited in DFS.
 *		@pupils: All pupils of this member.
 *		@mentors: All mentors of this member.
 *		@tree_node: The respective node in the tree.
 */
typedef struct node3 {
	int index;
	int visited;
	edge *pupils;
	edge *mentors;
	tree_node *n;
} member;

// The root of tree.
tree_node *root = NULL;

// Define stack.
int *stack = NULL;
int top = -1;


// All the member.
member *members;

// The number of member.
int N;

/*
 * This is the initial function. It initiates the stack and 
 * set up the graph.
 * Parameters:
 *		None.
 * Returns:
 *		None.
 */
void init(void) {
	scanf("%d", &N);
	
	stack = (int*) malloc(sizeof(int) * (N + 1));
	
	members = (member*) malloc(sizeof(member) * (N + 1));
	members[0].visited = 0;
	members[0].pupils = NULL;
	members[0].mentors = NULL;
	members[0].index = 0;
	members[0].n = NULL;
	
	for(int i = 1; i <= N; i++) {
		members[i].visited = 0;
		members[i].pupils = NULL;
		members[i].mentors = NULL;
		members[i].index = i;
		members[i].n = NULL;
		
		int K;
		scanf("%d", &K);
		
		for(int j = 0; j < K; j++) {
			int A;
			scanf("%d", &A);
		
			edge *e1 = (edge*) malloc(sizeof(edge));
			e1->dst = A;
			e1->next = members[i].mentors;
			members[i].mentors = e1;
			
			edge *e2 = (edge*) malloc(sizeof(edge));
			e2->dst = i;
			e2->next = members[A].pupils;
			members[A].pupils = e2;
		}
	}	
}

/*
 * This function get the result of topo-sort of graph using DFS.
 * Parameters:
 *		@m: The current node to search.
 * Returns:
 *		None.
 */
void topo_sort(member *m) {
	m->visited = 1;
	edge *e = m->pupils;
	while(e != NULL) {
		if(members[e->dst].visited == 0) {
			topo_sort(&members[e->dst]);
		}
		e = e->next;
	}	
	
	stack[++top] = m->index;
}

/*
 * This function finds the Latest Common Ancient of the mentors
 * of given node.
 * Parameters:
 *		@n1: The member to search.
 * Returns:
 *		The index of the Latest Common Ancient node.
 */
int LCA(member *n1) {
	int min_depth = -1;
	tree_node *p[N];
	
	edge *e = n1->mentors;
	
	int cnt = 0;
	while(e != NULL) {
		p[cnt++] = members[e->dst].n;
		if(min_depth == -1 || min_depth > members[e->dst].n->depth) {
			min_depth = members[e->dst].n->depth;
		}
		e = e->next;
	}
	
	for(int i = 0; i < cnt; i++) {
		while(p[i]->depth > min_depth) {
			p[i] = p[i]->parent;
		}
	}
	
	while(1) {
		int i;
		for(i = 1; i < cnt; i++) {
			if(p[i] != p[0]) {
				break;
			}
		}
		
		if(i >= cnt) {
			break;
		}
		
		for(i = 0; i < cnt; i++) {
			p[i] = p[i]->parent;
		}
	}
	
	if(p[0] == NULL) {
		return -1;
	} else {
		return p[0]->value;
	}
}

/*
 * This function set up tree according to graph.
 * Parameters:
 *		None.
 * Returns:
 *		None.
 */
void set_tree(void) {
	while(top > -1) {
		member *cur = &members[stack[top]];
		top--;
		
		tree_node *n = (tree_node*) malloc(sizeof(tree_node));
		n->child = NULL;
		n->brother = NULL;
		n->parent = NULL;
		n->value = cur->index;
		
		cur->n = n;
		
		if(root == NULL) {
			root = n;
			n->depth = 0;
			n->parent = NULL;
		} else {
			int anci = LCA(cur);
			tree_node *p = members[anci].n;
			n->brother = p->child;
			p->child = n;
			n->parent = p;
			n->depth = p->depth + 1;
		}
	}	
}

/*
 * The main program.
 */
int main(void) {
	
	init();
	
	topo_sort(&members[0]);
	
	set_tree();
	
	int count = 0;
	tree_node *n = root->child;
	while(n != NULL) {
		count++;
		n = n->brother;
	}
	
	printf("%d\n", count);
	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值