并查集 最大集合并且按顺序输出集合内元素。

00033:Problem B 多线程并发

  • 查看
  • 提交
  • 统计
  • 提问
总时间限制:
1000ms
内存限制:
65536kB
描述

何谓并发?

最简单和最基本的并发,是指两个或多个独立的活动同时发生,并发在生活中随处可见,我们可以一边说话一边走路,也可以两只手同时作不同的动作,还有我们每个人过着相互独立的生活----操作系统老师站在讲台上讲着多任务操作系统,而你在下面拿着手机偷偷的看着库里逆天,;-)。

计算机领域中的并发是指在单个系统里同时执行多个独立的任务,而非顺序的进行一些活动。

有一个概念需要解释,关于并发与并行。

并发是指程序的逻辑结构。非并发的程序就是一根竹竿捅到底,只有一个逻辑控制流,也就是顺序执行的(Sequential)程序,在任何时刻,程序只会处在这个逻辑控制流的某个位置。而如果某个程序有多个独立的逻辑控制流,也就是可以同时处理多件事情,我们就说这个程序是并发的。这里的“同时”,并不一定要是真正在时钟的某一时刻(那是运行状态而不是逻辑结构),而是指,如果把这些逻辑控制流画成时序流程图,它们在时间线上是可以重叠的。

而并行指的是程序的运行状态。如果一个程序在某一时刻被多个CPU流水线同时进行处理,那么我们就说这个程序是以并行的形式在运行。(严格意义上讲,我们不能说某程序是“并行”的,因为“并行”不是描述程序本身,而是描述程序的运行状态,但这篇小文里就不那么咬文嚼字,以下说到“并行”的时候,就是指代“以并行的形式运行”)显然,并行一定是需要硬件支持的。

《ComputerSystems : A Programmer's Perspective 》有张图解释的很清楚,如下所示。

并查集 最大集合并且按顺序输出集合内元素。_第1张图片

下面我们只简单的介绍下多线程并发。

为什么要使用并发呢?最主要的一点是为了性能,多核处理器的成本越来越低,计算机的硬件的更新换代也没有之前那么迅速,为了更好的运行日益复杂的应用程序,程序员只能设计多任务并发式的软件。

有两种方式利用并发提高性能

1.将一个单个任务分成几部分,并且各自并行运行,降低总运行时间(一个线程执行算法的一部分,而另外一个线程执行算法的另一部分)。这属于任务并发。

2.每个线程在不同的数据部分执行相同的操作,也可以降低总的运行时间。这属于数据并发。

并发介绍完之后,我们来看看在C++中是如何使用并发和多线程的。当然,只有在C++11的标准下,才能编写出不依赖平台拓展的多线程代码。

并查集 最大集合并且按顺序输出集合内元素。_第2张图片            

解释下这段代码

1. C++11标准库中对多线程支持的声明在新的头文件中:管理线程的函数和类在中声明,而保护共享数据的函数和类在其他头文件中声明。

2. 打印信息的代码被移动到了一个独立的函数hello_functio()中,因为每个线程都必须具有一个初始函数(initial function),新线程的执行在这里开始。

3. 对于应用程序来说,初始线程是main(),但是对于其他线程,可以在std::thread对象的构造函数中指定——在本例中,被命名为t的std::thread对象拥有新函数hello_function()作为其初始函数

4. 与直接写入标准输出或是从main()调用hello_function()不同,该程序启动了一个全新的线程来实现,将线程数量一分为二,初始线程始于main(),而新线程始于hello_function()

这样我们就有两个线程,一个主线程什么负责初始化线程对象t,线程t负责执行线程函数hello_function()。理解到这里后,我们引入另外一个新的概念,由于线程t是在主线程中来执行的,所以我们规定主线程是t线程的父线程。相应的,t线程是主线程的子线程。

在此基础上给出我们的问题


输入
第一行输入一个N(0≤N≤100000),表示有N个线程(1,2,...,N)
接下来第二行输入一个M,表示有M组”父线程-子线程”的关系
接下来m行,每行为两个不同的整数A,B(0≤A,B≤100000),代表线程A是线程B的父线程。
输出
第一行输出一个正整数Max_N,表示拥有最多线程数的”父线程-子线程”模块的线程数量;
接下来一行按升序输出该”父线程-子线程”模块的所有线程。
样例输入
15101 41 51 66 36 27 87 97 1014 1514 13
样例输出
61 2 3 4 5 6
 
  
详情看代码。好多事模板,但是重点一个k用的很巧妙,直接找到孙子点,一层一层找回去祖先。还有在过程中就把大小比较好,可以避免输出时还要比较的麻烦。
 
  
#include
#include
#include
#include
using namespace std;
int par[100005];
int ran[100005];
int ans=0, k;

int find(int x) //c查询树的根
{
	int r = x;
	while (r != par[r])
		r = par[r];
	/*int i = x, j;//路径压缩??
	while (par[i] != r)
	{
		j = par[i];
		par[i] = r;
		i = j;
	}*/
	return r;
}
void unite(int x, int y)
{
	x = find(x);
	y = find(y);
	if (x == y)
        return;
	if (x > y)
	{
		par[x] = y;
		ran[y] += ran[x];
		if (ans < ran[y])
		{
			ans = ran[y];
			k = y;//存取根节点
		}
	}
	else
    {
		par[y] = x;
		ran[x] += ran[y];
		if (ans < ran[x])
        {
			ans = ran[x];
			k = x;
		}
	}
}
bool same(int x, int y) //判断是不是一个集合
{
	return find(x) == find(y);
}
int main()
{
	int N, M;
	scanf("%d%d",&N,&M)
	for (int i = 0; i<=N; i++)
    {
		par[i] = i;//父节点
		ran[i] = 1;//高度
	}
	for (int i = 0; i

你可能感兴趣的:(数据结构)