在一个社区里,每一个人都有本身的小圈子,还可能同时属于不少不一样的朋友圈。咱们认为朋友的朋友都算在一个部落里,因而要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?而且检查任意两我的是否属于同一个部落。php
输入在第一行给出一个正整数N(≤1e4),是已知小圈子的个数。随后N行,每行按下列格式给出一个小圈子里的人:html
K P[1] P[2] ⋯ P[K]ios
其中K是小圈子里的人数,P[i](i=1,⋯,K)是小圈子里每一个人的编号。这里全部人的编号从1开始连续编号,最大编号不会超过104。c++
以后一行给出一个非负整数Q(≤1e4),是查询次数。随后Q行,每行给出一对被查询的人的编号。算法
首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,若是他们属于同一个部落,则在一行中输出Y
,不然输出N
。数据结构
4 3 10 1 2 2 3 4 4 1 5 7 8 3 9 6 4 2 10 5 3 7
10 2 Y N
运用 并查集 解决问题函数
在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。有一个联合-查找算法(union-find algorithm)定义了两个用于此数据结构的操做:post
Find
:肯定元素属于哪个子集。它能够被用来肯定两个元素是否属于同一子集。Union
:将两个子集合并成同一个集合。因为支持这两种操做,一个不相交集也常被称为联合-查找数据结构(union-find data structure)或合并-查找集合(merge-find set)。其余的重要方法,MakeSet
,用于建立单元素集合。有了这些方法,许多经典的划分问题能够被解决.spa
通俗讲,并查集 可以判断判断两个角色在不在一个群组里面 .code
const int N = 10010; //初始化 int fa[N]; //每一个结点 int Rank[N]; //树的高度/节点的秩 void MakeSet() //对n个结点初始化 { for (int i = 0; i < N; i++) { fa[i] = i; //每一个结点的上级都是本身 Rank[i] = 1; //每一个结点构成的树的高度为1 } } //改进查找算法:完成路径压缩,将x的上级直接变为根结点,那么树的高度就会大大下降 int Find(int x) //查找结点x的根结点 { if (fa[x] == x) { //递归出口:x的上级为x自己,即x为根结点 return x; } return fa[x] = Find(fa[x]); //递归查找 此代码至关于 先找到根结点rootx,而后pre[x]=rootx } //判断两个结点是否连通 bool Judge(int x, int y) { return Find(x) == Find(y); //判断两个结点的根结点(亦称表明元)是否相同 } void Union(int x, int y) { x = Find(x); y = Find(y); if (x == y) { return; } if (Rank[x] > Rank[y]) { fa[y] = x; //令y的根结点的上级为x } else { if (Rank[x] == Rank[y]) { Rank[y]++; } fa[x] = y; } }
在算法竞赛的实际代码中,即使不使用启发式合并,代码也每每可以在规定时间内完成任务。所以咱们通常状况下无需使用启发式合并(按秩合并).
C++中库函数大多为下划线命名法,所以本身的函数推荐驼峰命名法.
#include <bits/stdc++.h> using namespace std; const int MAXN = 10010; //并查集初始化 int fa[MAXN]; //无需按秩合并 void MakeSet() { for (int i = 0; i < MAXN; i++) { fa[i] = i; } } //查找其父节点 int Find(int x) { if (fa[x] == x) { return x; } else { return fa[x] = Find(fa[x]); } } //合并两个节点/集合 void Union(int x, int y) { x = Find(x); y = Find(y); if (x != y) { fa[x] = y; } } //判断两个元素是否有相同的父节点 bool Judge(int x, int y) { return Find(x) == Find(y); } int main(void) { MakeSet(); set<int> member; int n; cin >> n; for (int i = 0; i < n; i++) { int input; vector<int> temp; int k; cin >> k; for (int j = 0; j < k; j++) { cin >> input; temp.push_back(input); member.insert(input); } for (auto &&j : temp) { Union(input, j); } temp.clear(); } int count(0); cout << member.size() << ' '; for (auto &&i : member) { if (fa[i] == i) { count++; } } cout << count << endl; int k; cin >> k; for (int i = 0; i < k; i++) { int t1, t2; cin >> t1 >> t2; if (Judge(t1, t2)) { cout << 'Y' << endl; } else { cout << 'N' << endl; } } return 0; }
对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?
,最长对称子串为s PAT&TAP s
,因而你应该输出11。
输入在一行中给出长度不超过1000的非空字符串。
在一行中输出最长对称子串的长度。
Is PAT&TAP symmetric?
11
string Manacher(string ori_s) { string s = "#"; for (auto c : ori_s) { s += c; s += "#"; } int N = s.size(); //N 始终为奇数 vector<int> radius(N, 0); //radius(半径)为N个0 int C = 0; int R = 0; int max_center = -1; int max_radius = 0; for (int i = 0; i < N; ++i) { if (i < R) { radius[i] = min(R - i, radius[2 * C - i]); } while (i + radius[i] + 1 < N && i - radius[i] - 1 >= 0 && s[i + radius[i] + 1] == s[i - radius[i] - 1]) { ++radius[i]; } if (radius[i] + i > R) { R = radius[i] + i; C = i; } if (radius[i] > max_radius) { max_radius = radius[i]; max_center = i; } } string res; for (int i = -max_radius; i <= max_radius; ++i) { if (s[i + max_center] != '#') { res += s[i + max_center]; } } return res; }
然而我暂时还理解不了马拉车算法 , 因此我这里使用朴素算法给出题解
#include <iostream> #include <vector> #include <algorithm> using namespace std; string LongestPalindrome(string ori) { if (ori.empty()) { return ""; } string s; for (auto &&i : ori) { s.push_back('#'); s.push_back(i); } s.push_back('#'); vector<int> p; for (auto i = s.begin(); i != s.end(); i++) { int count(1); for (auto front = i + 1;; front++) { auto back = i - (front - i); if (distance(s.begin(), back) >= 0 && distance(front, s.end()) > 0 && *back == *front) { count++; } else { break; } } p.push_back(count); } vector<int>::iterator result = max_element(p.begin(), p.end()); string temp; int len = *result - 1; for (auto i = s.begin() + distance(p.begin(), result) - len; i != s.begin() + distance(p.begin(), result) + len; i++) { if (*i != '#') { temp.push_back(*i); } } return temp; } int main() { string str; getline(cin, str); cout << LongestPalindrome(str).size(); return 0; }
Ubuntu20.04 正式发布了,ZLS 是一个做死小能手,因而他决定尝试一下这个船新版本。好不容易装完系统,ZLS 想要给他的系统装一些经常使用的软件。众所周知,在 Linux 装软件会遇到各类奇奇怪怪的依赖问题(所谓依赖问题就是若A依赖B,则B要先与A安装)。ZLS 对此不厌其烦,所以他想知道他要用什么顺序安装软件,能够一次安装成功呢?
Tips: ZLS 还有一个癖好,他喜欢先安装字典序小的软件。
第一行包含一个正整数 T 表示数据组数。 每组数据的第一行包 n 和 m, 表示有 n 个软件,m 个依赖关系。 接下来的一行包含 n 个软件名(软件名仅包含小写字母 a-z ) 接下来的 m 行每行有两个软件名 s 和 t,表示 t 依赖 s ,即 s 要在 t 以前安装。 数据保证: 1≤T≤51 \le T \le 51≤T≤5 1≤n≤3×104,1≤m≤1051 \le n \le 3 \times 10^{4}, 1 \le m \le 10^{5}1≤n≤3×104,1≤m≤105 1≤∣s∣,∣t∣≤101 \le |s|,|t| \le 101≤∣s∣,∣t∣≤10
输出描述:
共 T 组输出,每组输出先输出一行 Case #%d: ,%d 替换为当前输出的组数。 接下来是 n 行,按照安装的顺序输出。 若是没法进行安装,输出 Impossible (注意大小写)。
示例1
输入
2 4 2 a b c d a b b c 3 3 a b c a b b c c a
输出
Case #1: a b c d Case #2: Impossible
软件的依赖关系能够看做一个有向图,而软件安装顺序就是求有向图的一个拓扑排序。注意题目中要求按照字典序排序,所以拓扑排序中要用优先队列。对于字符串的处理,能够先映射成整数,再作拓扑排序。
有的同窗反映超时,能够试试看unordered_map,比map要少个log。
先去了解 浅显易懂的拓扑排序
#include <bits/stdc++.h> #define MAXN 30005 using namespace std; string soft[MAXN]; // 存全部软件 , 软件名和下表index 一一对应 unordered_map<string, int> _index; // 一一对应 关系 vector<int> edge[MAXN]; // DAG的边, 存的是依赖于它的全部软件 int in_degree[MAXN]; //每个软件的入度 void DAG_init(const int &n, const int &m) { _index.clear(); for (int i = 0; i < n; i++) { cin >> soft[i]; //每次进行初始化清空 in_degree[i] = 0; edge[i].clear(); } sort(soft, soft + n, greater<string>()); // 字典序排列 for (int i = 0; i < n; ++i) _index[soft[i]] = i; for (int i = 0; i < m; i++) { string a, b; cin >> a >> b; edge[_index[a]].push_back(_index[b]); ++in_degree[_index[b]]; } } void TopologicalSort(const int &n) { static int num = 0; // Case #%d: priority_queue<int> q; for (int i = 0; i < n; i++) if (in_degree[i] == 0) // 入度=0,入队 q.push(i); vector<int> ans; while (!q.empty()) { int now = q.top(); q.pop(); ans.push_back(now); for (auto &&i : edge[now]) if (--in_degree[i] == 0) q.push(i); } cout << "Case #" << ++num << ":\n"; if (ans.size() != n) //没有成环 { cout << "Impossible" << endl; return; } for (auto &&i : ans) cout << soft[i] << endl; } int main(void) { ios::sync_with_stdio(0); int t; cin >> t; for (int i = 0; i < t; i++) { int n, m; cin >> n >> m; DAG_init(n, m); TopologicalSort(n); } return 0; }
西安邮电大学有一辆从老校区到新校区的校车,总共有 n 个学生乘坐校车,在 aia_{i}ai 站上车,在 bib_{i}bi 站下车。学校打算去除一部分没必要要的站点,请问须要保留多少站点,须要安排多少个座位?
输入 T 组数据 (1≤T≤10)(1 \le T \le 10)(1≤T≤10) 输入 n(1≤n≤105)n(1 \le n \le 10^{5})n(1≤n≤105) 输入 n 组 ai,bi(1≤ai,bi≤109)a_{i},b_{i}(1 \le a_{i},b_{i} \le 10^{9})ai,bi(1≤ai,bi≤109)
输出描述:
输出保留站点数,座位数。
示例1
输入
1 3 1 2 1 3 2 4
输出
4 2