连通性一·割边与割点
题目传送:hihoCoder - 1183 - 连通性一·割边与割点
AC代码:
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 20005;
int n, m;
vector<int> G[maxn];
int dfn[maxn];//用于标记访问次序
int low[maxn];//low[u]为u及其后代所能连回的最早的祖先的dfn值
int vis[maxn];//标记
int pa[maxn];//记录父亲结点
int is_cut[maxn];//记录是否为割点
struct node {
int u, v;
}ans_b[200005];
int ans_bian;
bool cmp(node a, node b) {
if(a.u == b.u)return a.v < b.v;
return a.u < b.u;
}
int cur_index;
void dfs(int u) {
dfn[u] = low[u] = ++ cur_index;
vis[u] = 1;
int child = 0;
int d = G[u].size();
for(int i = 0; i < d; i ++) {
int v = G[u][i];
if(!vis[v]) {
child ++;
pa[v] = u;
dfs(v);
low[u] = min(low[u], low[v]);
if(pa[u] == 0 && child > 1) {
is_cut[u] = 1;//这里的割点不能存在一个数组里,如果硬要存的话,则需要判重
}
if(pa[u] != 0 && low[v] >= dfn[u]) {
is_cut[u] = 1;
}
if(low[v] > dfn[u]) {//而割边的话可以用数组存,因为边只访问一次,不会重复,而点可能访问多次
ans_b[ans_bian].u = min(u, v);
ans_b[ans_bian].v = max(u, v);
ans_bian ++;
}
}
else if(v != pa[u]){
low[u] = min(dfn[v], low[u]);
}
}
}
int main() {
scanf("%d %d", &n, &m);
int u, v;
for(int i = 0; i < m; i ++) {
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
cur_index = 0;
ans_bian = 0;
memset(pa, 0, sizeof(pa));
memset(vis, 0, sizeof(vis));
dfs(1);
//for(int i = 1; i <= n; i ++) cout << low[i] << " "; cout << endl;
int flag = 0;
for(int i = 1; i <= n; i ++) if(is_cut[i]){
if(flag) printf(" ");
flag = 1;
printf("%d", i);
}
if(!flag) printf("Null");
printf("\n");
//cout << "haha" << endl;
sort(ans_b, ans_b + ans_bian, cmp);
for(int i = 0; i < ans_bian; i ++) {
printf("%d %d\n", ans_b[i].u, ans_b[i].v);
}
return 0;
}
危险系数
题目传送:蓝桥杯 - 历届试题 危险系数
题目大意:去求有多少个点,使得去掉这个点之后,U,V不再连通。
分析:类似割点,这里因为数据量较小,可以暴力标记所有从U到V的路径,然后给每一个点记录一个权值,权值的含义为这个点在多少条从U到V的路径上,同时用cnt记录有多少条从U到V的路径,最后只要某个点的权值等于cnt的话,说明这个点就是U和V的割点,因为它在从U到V的所有路径上。
注意:还有不连通的情况要特判一下,即cnt=0的时候
AC代码:
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 1005;
int n, m;
vector<int> G[maxn];//整个图
int way[maxn];//记录每一条路的各个结点
int sum[maxn];//记录每个节点在多少条从U到V的路径上
int vis[maxn];//标记数组
int U, V;
int cnt;//用于记录从U到V的路径的个数
void dfs(int u, int n) {//深搜找所有路径
way[n] = u;
if(u == V) {//找到一条路径,并return
cnt ++;
for(int i = 0; i <= n; i ++) {
sum[way[i]] ++;
}
}
int d = G[u].size();
for(int i = 0; i < d; i ++) {
int v = G[u][i];
if(!vis[v]) {
vis[v] = 1;
dfs(v, n + 1);
vis[v] = 0;
}
}
}
int main() {
scanf("%d %d", &n, &m);
int u, v;
for(int i = 0; i < m; i ++) {
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
scanf("%d %d", &U, &V);
memset(vis, 0, sizeof(vis));
memset(sum, 0, sizeof(sum));
vis[U] = 1;
cnt = 0;
dfs(U, 0);
if(cnt == 0) {//特判不连通的情况
printf("-1\n");
return 0;
}
int ans = 0;
for(int i = 1; i <= n; i ++) {
//cout << sum[i] << endl;
if(sum[i] == cnt) {//cnt是总的路径个数,如果相等的话,说明i点在U到V的每一条路径上,即删去改点后U,V不连通
ans ++;
}
}
printf("%d\n", ans - 2);//减2是因为起始点和结束点不应该包括在里面
return 0;
}