00033:Problem B 多线程并发
-
总时间限制:
- 1000ms 内存限制:
- 65536kB
-
描述
-
何谓并发?
最简单和最基本的并发,是指两个或多个独立的活动同时发生,并发在生活中随处可见,我们可以一边说话一边走路,也可以两只手同时作不同的动作,还有我们每个人过着相互独立的生活----操作系统老师站在讲台上讲着多任务操作系统,而你在下面拿着手机偷偷的看着库里逆天,;-)。
计算机领域中的并发是指在单个系统里同时执行多个独立的任务,而非顺序的进行一些活动。
有一个概念需要解释,关于并发与并行。
并发是指程序的逻辑结构。非并发的程序就是一根竹竿捅到底,只有一个逻辑控制流,也就是顺序执行的(Sequential)程序,在任何时刻,程序只会处在这个逻辑控制流的某个位置。而如果某个程序有多个独立的逻辑控制流,也就是可以同时处理多件事情,我们就说这个程序是并发的。这里的“同时”,并不一定要是真正在时钟的某一时刻(那是运行状态而不是逻辑结构),而是指,如果把这些逻辑控制流画成时序流程图,它们在时间线上是可以重叠的。
而并行指的是程序的运行状态。如果一个程序在某一时刻被多个CPU流水线同时进行处理,那么我们就说这个程序是以并行的形式在运行。(严格意义上讲,我们不能说某程序是“并行”的,因为“并行”不是描述程序本身,而是描述程序的运行状态,但这篇小文里就不那么咬文嚼字,以下说到“并行”的时候,就是指代“以并行的形式运行”)显然,并行一定是需要硬件支持的。
《ComputerSystems : A Programmer's Perspective 》有张图解释的很清楚,如下所示。
下面我们只简单的介绍下多线程并发。
为什么要使用并发呢?最主要的一点是为了性能,多核处理器的成本越来越低,计算机的硬件的更新换代也没有之前那么迅速,为了更好的运行日益复杂的应用程序,程序员只能设计多任务并发式的软件。
有两种方式利用并发提高性能
1.将一个单个任务分成几部分,并且各自并行运行,降低总运行时间(一个线程执行算法的一部分,而另外一个线程执行算法的另一部分)。这属于任务并发。
2.每个线程在不同的数据部分执行相同的操作,也可以降低总的运行时间。这属于数据并发。
并发介绍完之后,我们来看看在C++中是如何使用并发和多线程的。当然,只有在C++11的标准下,才能编写出不依赖平台拓展的多线程代码。
解释下这段代码
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<iostream> #include<cstdio> #include<queue> #include<cstring> 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<M; i++) { int a, b; scanf("%d%d", &a, &b); unite(a, b); } printf("%d\n", ans); int an = 0; for (int i = 1; i<=N; i++) { if (same(k, i)) { an++; printf("%d", i); if (an == ans) { printf("\n"); break; } printf(" "); } } return 0; }