题集 (最短路 并查集扩展)
题单
A Coloring Contention(Gym 102433C)
最短路 + 链式前向星存储
题目意思是
使用能够使Bob变换最多次数的方法,求出Bob变换的次数,已知是最优,那么Bob第一步确定颜色,其余每一步必须改变颜色,所以两点间距离可以设置为1,求出Bob到达终点的距离然后减1就可以了,本题我用的链式前向星用来存储数据,跑完即可
#include <iostream>
#include <memory.h>
#include <queue>
using namespace std;
const int M = 100010;
const int INF = 0x3f3f3f3f;
struct node {
int u, v, next1;
}edge[2 * M];
int head[M * 2], cnt, dis[M * 2];
bool vis[M];
int n, m;
void init() {
for (int i = 1; i <= n; ++i) {
head[i] = -1;
}
cnt = 0;
}
void addNode(int u, int v) {
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].next1 = head[u];
head[u] = cnt++;
}
void spfa(int start1) {
queue<int> que;
dis[start1] = 0;
vis[start1] = true;
que.push(start1);
while (!que.empty()) {
int now1 = que.front();
que.pop();
if (now1 == n)
return;
for (int i = head[now1]; i != -1; i = edge[i].next1) {
if (!vis[edge[i].v]) {
dis[edge[i].v] = dis[edge[i].u] + 1;
vis[edge[i].v] = 1;
que.push(edge[i].v);
}
}
//cout << "#";
}
}
int main() {
cin >> n >> m;
init();
int u, v, w;
for (int i = 0; i < m; ++i) {
cin >> u >> v;
addNode(u, v);
addNode(v, u);
}
spfa(1);
cout << dis[n] - 1;
return 0;
}
B Jzzhu and Cities(CodeForces 449B)
迪杰斯特拉 + 链式前向星存储
本题是先输入一些不能被删除的道路,然后输入一些能够被删除的铁路,使用链式前向星存储道路,dijkstra跑完全程,在跑的时候记录下来每个节点的入度,跑完后,查看铁路,如果铁路长度大于到铁路终点的最短路,那么必不可能参与最短路的组成,最终值加1,如果铁路长度等于到铁路终点的最短路但是铁路终点的入度是1,那么也就说明,该铁路不能被删除,如果铁路终点大于1,则说明该点可以被删除
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
const ll M = 100010;
const ll INF = 0x3f3f3f3f;
struct Edge {
ll u, v, w, next, flag;
}edge[M * 10];
ll head[M * 2], dis[M * 2], nodev[M], nodew[M];
ll n, m, k, cnt = 0;
int in[M * 2];
void addEdge(ll u, ll v, ll w, ll flag) {
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].w = w;
edge[cnt].flag = flag;
edge[cnt].next = head[u];
head[u] = cnt++;
}
bool vis[M * 2];
bool vis1[M * 2];
struct node {
ll H;
ll u;
ll flag;
bool operator < (const node& x) const {
return H > x.H;
}
}start1, now;
void init() {
for (ll i = 0; i <= n; ++i) {
head[i] = -1;
dis[i] = INF;
}
}
ll ans = 0;
void dij() {
priority_queue<node> que;
start1.H = 0;
start1.u = 1;
que.push(start1);
dis[start1.u] = 0;
while (!que.empty()) {
now = que.top();
que.pop();
if (vis[now.u]) continue;
vis[now.u] = 1;
//if (now.flag == 1) {
// ans++;
//}
for (ll i = head[now.u]; i != -1; i = edge[i].next) {
if (dis[edge[i].v] > dis[edge[i].u] + edge[i].w && !vis[edge[i].v]) {
dis[edge[i].v] = dis[edge[i].u] + edge[i].w;
now.H = dis[edge[i].v];
now.u = edge[i].v;
now.flag = edge[i].flag;
que.push(now);
// 入度刷新
in[edge[i].v] = 1;
//if(now.u)
}
else if (dis[edge[i].v] == dis[edge[i].u] + edge[i].w) {
in[edge[i].v]++;
}
}
}
}
int main() {
scanf("%lld%lld%lld", &n, &m, &k);
ll u, v, w;
init();
for (ll i = 0; i < m; ++i) {
scanf("%lld%lld%lld", &u, &v, &w);
addEdge(u, v, w, 0);
addEdge(v, u, w, 0);
}
for (ll i = 0; i < k; ++i) {
scanf("%lld%lld", &nodev[i], &nodew[i]);
addEdge(1, nodev[i], nodew[i], 1);
addEdge(nodev[i], 1, nodew[i], 1);
}
dij();
for (int i = 0; i < k; ++i) {
if (nodew[i] > dis[nodev[i]]) {
ans++;
}
else if (nodew[i] == dis[nodev[i]] && in[nodev[i]] > 1) {
ans++;
in[nodev[i]]--;
}
}
cout << ans << endl;
return 0;
}
C Find them, Catch them(POJ 1703)
构造并查集
本题有两种输入,一种是A用来询问两个罪犯的帮派,一种是D告诉我们两个罪犯不是同一个帮派。
那么我们可以将每个罪犯a设置为一个帮派每个罪犯a+n设置为另一个帮派,那么在每次询问时如果a和b帮派寻找父亲的时候是一样的说明为一个帮派,如果a和b+n帮派寻找父亲的时候是一样的说明不为一个帮派,剩下的一种情况就是还不确定帮派.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = 100010;
int pre[M * 2];
int n, m;
int find(int son) {
/*路径压缩*/
int deson, dad1;
deson = son;
while (son != pre[son]) son = pre[son]; // 直到找到爷爷节点
// 一直更新到爷爷节点 期间每个点都更新成爷爷节点
while (deson != pre[deson]) {
dad1 = pre[deson];
pre[deson] = son;
deson = dad1;
}
return son;
}
void init(int n) {
for (int i = 1; i <= 2 * n; ++i) {
pre[i] = i;
}
}
int main() {
int t, dada1, dada2, dadb1, dadb2, a, b;
char c;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
init(n);
for (int i = 0; i < m; ++i) {
getchar();
c = getchar();
if (c == 'A') {
scanf("%d%d", &a, &b);
dada1 = find(a);
dada2 = find(a + n);
dadb1 = find(b);
dadb2 = find(b + n);
if (dada1 == dadb1) printf("In the same gang.\n"); // 一个帮派
else if (dada1 == dadb2) printf("In different gangs.\n"); // 不同的帮派
else printf("Not sure yet.\n"); // 还不确定
}
else if (c == 'D') {
scanf("%d%d", &a, &b);
dada1 = find(a);
dada2 = find(a + n);
dadb1 = find(b);
dadb2 = find(b + n);
if (dada1 != dadb2) pre[dada1] = dadb2; // 将a 和b + n 设置为同一个帮派
if (dada2 != dadb1) pre[dada2] = dadb1; // 将a + n 和b设置为同一个帮派
}
}
}
return 0;
}
D Cube Stacking (POJ 1988)
带权并查集
题目链接
本题有两种操作,一种是’M’将第一个数所在的列的所有方块放到第二个的上面,另一种是’C’查询给出的数下面的方块数,这个题是个带权并查集,设置dis[i]用来保存i到i的父亲节点的距离,在路径压缩时将i的爷爷设置为i的父亲,并且更新dis[i],在每次查询时,用本列总共的块数减去查询点到父亲节点的距离,再减去自身,其中dis[x]是x到它父节点的距离也就是最高点的距离,因为已经压缩完毕后dis[x]的父节点就已经是最高点(爷爷)了,剩下的就是x下面的距离
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int M = 30030;
int pre[M], dis[M], num[M];
void init(int n) {
for (int i = 1; i <= n; ++i) {
pre[i] = i;
num[i] = 1; // 初始化num[i] = 1
}
}
int find(int son) {
if (son == pre[son]) return son; // 设置出口 找到爷爷
int tot = find(pre[son]); // 找到他的父亲
dis[son] += dis[pre[son]]; // 路径压缩时改变当前son到上一级的距离 因为爸爸要变成爷爷 所以它当前的距离为累加过来的
return pre[son] = tot; // 路径都压缩成爷爷
}
void merge(int x1, int x2) {
int dad1 = find(x1);
int dad2 = find(x2);
if (dad1 == dad2) return; // 如果已经在同一行不进行移动
/*把x1 移动到 x2*/
pre[dad2] = dad1;
dis[dad2] += num[dad1];
num[dad1] += num[dad2];
}
void query(int x) {
int dad = find(x); // 已经路径压缩了 所以x父节点已经改变为爷爷 也就是最高点
printf("%d\n", num[dad] - dis[x] - 1); // 本列总共的数量减去当前点到它父亲节点的距离 再减去自身 dis[x]是x到最高点的距离 因为已经压缩完毕他的父节点是最高点
}
int main() {
int op, x1, x2;
scanf("%d", &op);
init(M - 1);
char c;
while (op--) {
getchar();
c = getchar();
if (c == 'M') {
scanf("%d%d", &x1, &x2);
merge(x1, x2); // 把x1所在的放到x2
}
else if (c == 'C') {
scanf("%d", &x1);
query(x1);
}
}
return 0;
}
浙公网安备 33010602011771号