优先队列
优先队列就是让优先级高的出列。它是队列和排序的组合,不仅可以储存数据,还可以将这些数据按照设定的规则进行排序。每次push和pop操作,优先队列都会动态调整,把优先级最高的元素放在前面。
正因为优先队列这种神奇的性质,有些题目配合优先队列会有奇效
定义:priority_queue<Type, Container, Functional>
比较方式默认用operator< , , 所以如果你把后面俩个参数缺省的话,优先队列就是大顶堆,队头元素最大。(也就是默认非升序)
比较函数例子
struct cmp1
{
bool operator ()(int& a, int& b)
{
return a > b;//最小值优先
}
};
支持的操作:
q.empty() //如果队列为空,则返回true,否则返回false
q.size() //返回队列中元素的个数
q.pop() //删除队首元素,但不返回其值
q.top() //返回具有最高优先级的元素值,但不删除该元素
q.push(item) //在基于优先级的适当位置插入新元素
next_permutation()
求下一个排列组合的函数next_permutation().
例如3个字符a.b.c组成的序列,next_permutation()能按字典序返回6个组合,即abc,acb,bac,bca,cab,cba
例 HDU1027
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include <vector>
#include <cmath>
using namespace std;
int a[1001] = {0};
int main()
{
int n, m;
while (cin >> n >> m) {
for (int i = 1; i <= n; i++) {//生成一个字典序最小的序列
a[i] = i;
}
int b = 1;
do {
if (b == m)break;
b++;
} while (next_permutation(a + 1, a + n + 1));
for (int i = 1; i < n; i++) {
cout << a[i] << " ";
}
cout << a[n] << endl;
}
return 0;
}
拓扑排序
定义
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
步骤
从DGA图中找到一个没有前驱的顶点输出。(可以遍历,也可以用优先队列维护)
删除以这个点为起点的边。(它的指向的边删除,为了找到下个没有前驱的顶点)
重复上述,直到最后一个顶点被输出。如果还有顶点未被输出,则说明有环
例 HDU3342
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include <vector>
#include <cmath>
using namespace std;
const int maxn = 1000;
vector<int>G[maxn];
int n, m;
int in[maxn];
bool topo() {
queue<int>Q;
int sum = 0;
for (int i = 1; i <= n; i++) {
if (in[i] == 0)
Q.push(i);
}
while (!Q.empty()) {
int u = Q.front();
Q.pop();
sum++;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
--in[v];
if (in[v] == 0)
Q.push(v);
}
}
return sum == n;
}
int main()
{
int u, v;
while (scanf("%d%d", &n, &m)) {
if (n == 0)
break;
memset(in, 0, sizeof(in));
for (int i = 1; i <= n; i++) {
G[i].clear();
}
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u, &v);
u++;
v++;///标号从1开始
G[u].push_back(v);
in[v]++;
}
if (topo() == 1) {
puts("YES");
}
else {
puts("NO");
}
}
return 0;
}
并查集模板
这是我以前用的朴素写法的优化版
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include <vector>
#include <cmath>
using namespace std;
const int maxn = 1050;
int s[maxn];
int height[maxn];
void init_set() {
for (int i = 1; i <= maxn; i++) {
s[i] = i;
height[i] = 0;
}
}
int find_set(int x) {//并查集内查找
if (x != s[x])s[x] = find_set(s[x]);
return s[x];
//非递归做法
/*int r = x;
while (s[r] != r)r = s[r];
int i = x, j;
while (i != r) {
j = s[i];
s[i] = r;
i = j;
}
return r;*/
}
void union_set(int x, int y) {//合并
x = find_set(x);
y = find_set(y);
if (height[x] == height[y]) {
height[x] = height[y] + 1;
s[y] = x;
}
else {
if (height[x] < height[y])s[x] = y;
else s[y] = x;
}
}
int main()
{
int t, n, m, x, y;
cin >> t;
while (t--) {
cin >> n >> m;
init_set();
for (int i = 1; i <= m; i++) {
cin >> x >> y;
union_set(x, y);//x认识y,把他们合并起来
}
int ans = 0;
for (int i = 1; i <= n; i++)
if (s[i] == i)
ans++;
cout << ans << endl;
}
return 0;
}