第四章:图、堆、并查集
25分段 1154 1150 1146 1142 1126 1122 1118 1114
30分段 1155 1147 1139 1131 1111 1107
①常用头文件
#include <iostream>
#include <algorithmv>
#include <vector>
#include <set>
using namespace std;
②常用函数
- sort( ) bool cmp()
③常用方法
-
【注意点】同组图重复判断不同案例时记得重置初始数据
//初始 vector<int> v[1009]; //输入完毕 …… //重复利用判断 vector<int> in(v, v+n+1);
-
【注意点】多种情况利用if及else if 不要一直用if else
-
是否为连通图的判断
int cnt = 0;
vector<bool> visit;//记得resize
void is_E(int idx){
visit[idx] = true;
cnt++;
for(int i = 0 ; i < v[idx].size(); i++)
if(visit[v[idx][i]]== false)
is_E(v[idx][i]);
}
//当 cnt == v.size() 一般为n 时 为连通图
-
【注意点】哈密顿图及其变体类题型不能形成交叉环
//注意判断 k1 == n + 1 // 是否断路 是否遍历所有顶点 是否头尾相同成环 是否多环 if(flag && se.size() == n && lst[0] == lst[1] && k1 == n + 1)
-
【注意点】并查集 查操作与并操作
并查集大统计注意定义数据结构DATA和ans 和查询表visit方便遍历存在点
int find(int a){ if(a != fa[a]) fa[a] = find(fa[a]); return fa[a]; } void Union(int a, int b){ int faA = find(a); int faB = find(b); //第一、正常情况下 if(faA != faB) fa[faA] = faB; //第二、需要祖先id取最大或最小时 //取最小 if(faA < faB) fa[faB] = faA; else if(faB < faA) fa[faA] = faB; //取最大 if(faA < faB) fa[faA] = faB; else if(faB < faA) fa[faB] = faA; }
-
深度优先遍历找根到叶的所有路径
// idx 为下标 a[idx]为该点值 先序镜像 右子树优先如下 根右左 void dfs(int idx){ //判断是否到底 if(idx*2 > n && idx*2+1 > n){ if(idx <= n){ for(int i = 0 ; i < v.size(); i++){ if(i != 0) printf(" "); printf("%d", v[i]); } printf("\n"); } }else{ //右 v.emplace_back(a[idx*2+1]); dfs(idx*2+1); //回退 v.pop_back(); //左 v.emplace_back(a[idx*2]); dfs(idx*2); //回退 v.pop_back(); } }
-
最短路径Dijkstra【每个点到起点的最短距离】 + DFS【从结尾反找起点】
int dis[], dispre[], e[][]; bool visit[]; //前提初始化 默认e[][]已输入知距离 fill(dis, dis+length, inf); fill(visit, visit+length, false); void dijkstra(int st){ dis[st] = 0; for(int i = 0; i < n; i++)//进行前每个点的前置为自己 disPre[i] = i; for(int i = 0; i < n; i++){ int u = -1, minn = inf; for(int j = 0 ; j < n ; j++){//找到未遍历的头点 if(visit[j] == false && dis[j] < minn){ u = j; minn = dis[j]; } } //都遍历完毕无头点退出 if(u == -1) break; visit[u] = true; for(int v = 0; v < n ; v++){ if(e[u][v] + dis[u] < dis[v]){ dis[v] = e[u][v] + dis[u]; disPre[v] = u; //以下为多要素判断 及当距离相同时取时间更短的前置 //weight[v] = weight[u] + w[u][v]; /* }else if(e[u][v] + dis[u] == dis[v] && weight[v] > weight[u] + w[u][v]){ weight[v] = weight[u] + w[u][v]; disPre[v] = u; }*/ } } }
-
SPFA 最短路径快速寻法
const int maxn = 1010; const int inf = 99999999; int dis[maxn], G[maxn][maxn], post[maxn], indegree[maxn], pre[maxn]; int in[maxn];//indegree备份,用来判断SPFA之后的原始源点 vector<int> post[maxn];//存对应的指向边 //单一边值找最短 void SPFA(){ //初始化 queue<int> q; fill(dis, dis+maxn, inf); fill(pre, pre+maxn, -1); for(int i = 0; i < n; i++){ if(!indegree[i]){//入度为0原始源点入队 dis[i] = 0; q.push(i); } } while(!q.empty()){ int u = q.front(); q.pop(); for(int i = 0 ; i < post[u].size(); i++){ int v = post[u][i]; iindegree[v]--; if(!indegree[v]) q.push(v); if(dis[u] + G[u][v] < dis[v]){ dis[v] = dis[u] + G[u][v]; pre[v] = u; } } } } //边值加权值找最优 int w[maxn][maxn]; int weight[maxn][maxn]; void SPFA(){ queue<int> q; fill(dis, dis+manx, inf); fill(pre, pre+maxn, -1); for(int i = 0 ; i < n ; i++){ if(!indegree[i]){ dis[i] = 0; w[i] = 0; q.push(i); } } while(!q.empty()){ int u = q.front(); q.pop(); for(int i = 0 ; i < post[u].size(); i++){ int v = post[u][j]; indegree[v]--; if(!indegree[v]) q.push(v); if(dis[u]+G[u][v] < dis[v]){ dis[v] = dis[u]+G[u][v]; w[v] = w[u] + weight[u][v]; pre[v] = u; }else if(dis[u]+G[u][v] == dis[v]){ //大于号还是小于号根据具体题目要求判断 if(w[u]+weight[u][v] > 或 < w[v]){ dis[v] = dis[u]+G[u][v]; w[v] = w[u] + weight[u][v];//更新副权值 pre[v] = u;//判断为前驱 } } } } }
-
并查集找主要联系 Union(孩子, 祖先) 如果没有明显键值令其中一个孩子为开山鼻祖建立群落之间的联系
vector<int> fa, clubnum; int lead[1009] = {0}; //注意初始化 fa[i] = i;
④相应习题
//1154 注意flag set a[]的重置初始化
#include <iostream>
#include <vector>
#include <set>
using namespace std;
struct node{
int c1, c2;
};
int n, m, k;
int main(){
cin >> n >> m;
vector<node> v(m);
for(int i = 0; i < m; i++)
scanf("%d %d", &v[i].c1, &v[i].c2);
cin >> k;
while(k--){
int a[10009]={-1};
bool flag = true;
set<int> se;
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
se.insert(a[i]);
}
for(int i = 0; i < m; i++){
if(a[v[i].c1] == a[v[i].c2]){
flag = false;
break;
}
}
if(flag)
printf("%d-coloring\n", se.size());
else
printf("No\n");
}
return 0;
}
//1150 思路没错,优化寻找不存在路径可用二维数组判断对应dis值为0 vector暂存例
//注意用set保证每个点都被经过
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int e[300][300], n, m, k, ans = 99999999, ansid;
vector<int> v;
void check(int index) {
int sum = 0, cnt, flag = 1;
scanf("%d", &cnt);
set<int> s;
vector<int> v(cnt);
for (int i = 0; i < cnt; i++) {
scanf("%d", &v[i]);
s.insert(v[i]);
}
for (int i = 0; i < cnt - 1; i++) {
if(e[v[i]][v[i+1]] == 0) flag = 0;
sum += e[v[i]][v[i+1]];
}
if (flag == 0) {
printf("Path %d: NA (Not a TS cycle)\n", index);
} else if(v[0] != v[cnt-1] || s.size() != n) {
printf("Path %d: %d (Not a TS cycle)\n", index, sum);
} else if(cnt != n + 1) {
printf("Path %d: %d (TS cycle)\n", index, sum);
if (sum < ans)
ans = sum;
ansid = index;
}
} else {
printf("Path %d: %d (TS simple cycle)\n", index, sum);
if (sum < ans) {
ans = sum;
ansid = index;
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
int t1, t2, t;
scanf("%d%d%d", &t1, &t2, &t);
e[t1][t2] = e[t2][t1] = t;
}
scanf("%d", &k);
for (int i = 1; i <= k; i++) check(i);
printf("Shortest Dist(%d) = %d\n", ansid, ans);
return 0;
}
//1146 拓扑序列需要每个入列的入度为0,每个前序入列时的指向结点入度--
// 注意每次循环使用都要初始化temp入度
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m, a, b, k, flag = 0, in[1010];
vector<int> v[1010];
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d %d", &a, &b);
v[a].push_back(b);
in[b]++;
}
scanf("%d", &k);
for (int i = 0; i < k; i++) {
int judge = 1;
vector<int> tin(in, in+n+1);
for (int j = 0; j < n; j++) {
scanf("%d", &a);
if (tin[a] != 0) judge = 0;
for (int it : v[a]) tin[it]--;
}
if (judge == 1) continue;
printf("%s%d", flag == 1 ? " ": "", i);
flag = 1;
}
return 0;
}
//1142 题目大意误解 clique的要素,每个不同的点之间必有路径
// maximal clique是否为满 判断是否无法加入新的点使其成为clique
#include <iostream>
#include <vector>
using namespace std;
int e[210][210];
int nv, ne, m, k;
int main() {
scanf("%d %d", &nv, &ne);
for(int i = 0; i < ne; i++){
int v1, v2;
scanf("%d %d", &v1, &v2);
e[v1][v2] = e[v2][v1] = 1;
}
scanf("%d", &m);
for(int i = 0; i < m; i++){
scanf("%d", &k);
vector<int> v(k);
int hash[210] = {0};
bool isclique = true, ismaximal = true;
for(int j = 0 ; j < k; j++){
scanf("%d", &v[j]);
hash[v[j]] = 1;
}
for(int j = 0 ; j < k; j++){
if(!isclique) break;
for(int l = j + 1; l < k; l++){
if(e[v[j]][v[l]] == 0){
isclique = false;
printf("Not a Clique\n");
break;
}
}
}
if(!isclique) continue;
for(int j = 1 ; j <= nv; j++){
if(hash[j] == 0){
for(int l = 0 ; l < k; l++){
if(e[v[l]][j] == 0) break;
if(l == k - 1) ismaximal = false;
}
if(!ismaximal) break;
}
}
if(ismaximal)
printf("Yes\n");
else
printf("Not Maximal\n");
}
return 0;
}
//1126
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> v;//邻接表
vector<bool> visit;
int v_degree[509];
int n, m , v1, v2;
int cnt = 0, even = 0;
//判断是否为连通图 cnt == n 时联通
void is_E(int idx){
visit[idx] = true;
cnt++;
for(int i = 0; i < v[idx].size(); i++)
if(visit[v[idx][i]] == false)
is_E(v[idx][i]);
}
int main() {
scanf("%d %d", &n, &m);
//重新分配大小防止段错误
v.resize(n+1);
visit.resize(n+1);
for(int i = 0; i < m; i++){
scanf("%d %d", &v1 ,&v2);
v[v1].emplace_back(v2);
v[v2].emplace_back(v1);
v_degree[v1]++;
v_degree[v2]++;
}
for(int i = 1; i <= n; i++){
if(i-1) printf(" ");
printf("%d", v_degree[i]);
if(v_degree[i]%2 == 0) even++;
}
printf("\n");
is_E(1);
if(even == n && cnt == n){
printf("Eulerian\n");
}else if(even == n - 2 && cnt == n){
//头尾奇数度
printf("Semi-Eulerian\n");
}else{
printf("Non-Eulerian\n");
}
return 0;
}
//1122
#include <iostream>
#include <set>
#include <vector>
using namespace std;
int n, m , v1, v2, k;
int e[209][209];
int main() {
scanf("%d %d", &n, &m);
for(int i = 0 ; i < m ; i++){
scanf("%d %d", &v1, &v2);
e[v1][v2] = e[v2][v1] = 1;
}
scanf("%d", &k);
for(int i = 0 ; i < k ; i++){
int a, flag = 1;
set<int> se;
scanf("%d", &a);
vector<int> v(a);
for(int j = 0; j < a ; j++)
scanf("%d", &v[j]);
for(int j = 0; j < a; j++){
se.insert(v[j]);
if(j == a - 1) break;
if(e[v[j]][v[j+1]] == 0){
flag = 0;
break;
}
}
//四个要素不能少,flag==1 头尾连接成坏 遍历所有点除首尾外无重复
if(flag == 1 && v[0] == v[a-1] && se.size() == n && a == n+1){
printf("YES\n");
}else{
printf("NO\n");
}
}
return 0;
}
//1118 第一种:利用并查集 第二种:利用DFS
#include <iostream>
using namespace std;
int n, m, k;
const int maxn = 10010;
int fa[maxn] = {0}, cnt[maxn] = {0};
bool exist[maxn];
//查操作
int find(int a) {
if(a != fa[a])
fa[a] = find(fa[a]);
return fa[a];
}
//并操作
void Union(int a, int b) {
if(find(a) != find(b)) fa[find(a)] = find(b);
}
int main() {
scanf("%d", &n);
//初始化 所有结点默认指向自己
for(int i = 1; i <= maxn; i++)
fa[i] = i;
int id, temp;
for(int i = 0; i < n; i++) {
scanf("%d%d", &k, &id);
exist[id] = true;
for(int j = 0; j < k-1; j++) {
scanf("%d", &temp);
Union(id, temp);
exist[temp] = true;
}
}
//输入结束
//一棵树选id作为树集代表算该数总共鸟数量
for(int i = 1; i <= maxn; i++) {
if(exist[i] == true) {
int root = find(i);
cnt[root]++;
}
}
int numTrees = 0, numBirds = 0;
for(int i = 1; i <= maxn; i++) {
if(exist[i] == true && cnt[i] != 0) {
numTrees++;
numBirds += cnt[i];
}
}
printf("%d %d\n", numTrees, numBirds);
//判断是否同树
scanf("%d", &m);
int ida, idb;
for(int i = 0; i < m; i++) {
scanf("%d%d", &ida, &idb);
printf("%s\n", (find(ida) == find(idb)) ? "Yes" : "No");
}
return 0;
}
//1114
#include <iostream>
#include <algorithm>
using namespace std;
int n, k, cnt, maxn = 10000;
struct DATA{
int id, fid, mid, num, area;
int cid[10];
}data[1009];
struct node{
int id, people;
double num, area;
bool flag = false;
}ans[10000];
int fa[10000];
bool visit[10000];
int find(int a){
if(a != fa[a]) fa[a] = find(fa[a]);
return fa[a];
}
void Union(int a, int b){
// if(find(a) != find(b)) fa[find(a)] = find(b);
int faA = find(a);
int faB = find(b);
// 为了输出id最小
if(faA < faB)
fa[faB] = faA;
else if(faB < faA)
fa[faA] = faB;
}
int cmp(node a , node b){
if(a.area != b.area)
return a.area > b.area;
else
return a.id < b.id;
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= maxn; i++)
fa[i] = i;
for(int i = 0; i < n; i++){
scanf("%d %d %d %d", &data[i].id, &data[i].fid, &data[i].mid, &k);
visit[data[i].id] = true;
if(data[i].fid != -1){
visit[data[i].fid] = true;
Union(data[i].fid, data[i].id);
}
if(data[i].mid != -1){
visit[data[i].mid] = true;
Union(data[i].mid, data[i].id);
}
for(int j = 0 ; j < k ; j++){
scanf("%d", &data[i].cid[j]);
visit[data[i].cid[j]] = true;
Union(data[i].cid[j], data[i].id);
}
scanf("%d %d", &data[i].num, &data[i].area);
}
for(int i = 0 ; i < n ; i++){
int id = find(data[i].id);
ans[id].id = id;
ans[id].num += data[i].num;
ans[id].area += data[i].area;
ans[id].flag = true;
}
for(int i = 0 ; i < 10000 ; i++){
if(visit[i])
ans[find(i)].people++;
if(ans[i].flag)
cnt++;
}
for(int i = 0 ; i < 10000 ; i++){
if(ans[i].flag){
ans[i].num = (double)(ans[i].num/ans[i].people);
ans[i].area = (double)(ans[i].area/ans[i].people);
}
}
//vector<node> ans时 sort(ans.begin(), ans.end(), cmp);
// bool cmp( node &a, node &b)
sort(ans, ans+10000, cmp);
printf("%d\n", cnt);
for(int i = 0 ; i < cnt; i++){
printf("%04d %d %.3f %.3f\n", ans[i].id, ans[i].people, ans[i].num, ans[i].area);
}
return 0;
}
//1155 先序镜像堆Heap
#include <iostream>
#include <vector>
using namespace std;
int n, m, k;
vector<int> v;
int a[1009];
bool is_min= false, is_max= false;
void dfs(int idx){
//判断是否到底
if(idx*2 > n && idx*2+1 > n){
if(idx <= n){
for(int i = 0 ; i < v.size(); i++){
if(i != 0) printf(" ");
printf("%d", v[i]);
}
printf("\n");
}
}else{
//右
v.emplace_back(a[idx*2+1]);
dfs(idx*2+1);
v.pop_back();
//左
v.emplace_back(a[idx*2]);
dfs(idx*2);
v.pop_back();
}
}
int main() {
cin >> n;
for(int i = 1 ; i <= n ; i++)
scanf("%d", &a[i]);
v.emplace_back(a[1]);
dfs(1);
for(int i = 2 ; i <= n ; i++){
if(a[i/2] > a[i]) is_max = true;
if(a[i/2] < a[i]) is_min = true;
}
if(is_max && is_min)
printf("Not Heap\n");
else if(is_max)
printf("Max Heap\n");
else if(is_min)
printf("Min Heap\n");
return 0;
}
//1147 Heap堆问题
#include <iostream>
#include <vector>
using namespace std;
int n, m, k;
int a[1009];
vector<int> ans;
bool is_max, is_min;
//postorder
void postorder(int idx){
if(idx*2 <= m) postorder(idx*2);
if(idx*2+1 <= m) postorder(idx*2+1);
ans.emplace_back(a[idx]);
}
int main() {
cin >> n >> m;
for(int i = 0 ; i < n ; i++){
is_max = is_min = false;
for(int j = 1 ; j <= m ; j++)
scanf("%d", &a[j]);
for(int j = 2 ; j <= m ; j++){
if(a[j/2] > a[j]) is_max = true;
if(a[j/2] < a[j]) is_min = true;
}
if(is_max && is_min)
printf("Not Heap\n");
else if(is_max)
printf("Max Heap\n");
else if(is_min)
printf("Min Heap\n");
postorder(1);
for(int j = 0 ; j < ans.size(); j++){
if(j != 0) printf(" ");
printf("%d", ans[j]);
}
printf("\n");
ans.clear();
}
return 0;
}
//1139 思路和题意理解有出入,判断是否同正同负可以用string t接受输入
// length(t1) != \ == length(t2)
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <unordered_map>
using namespace std;
int n, m, k;
unordered_map<int, bool> arr;
struct node{
int c, d;
};
bool cmp(node x, node y){
return x.c != y.c ? x.c < y.c : x.d < y.d;
}
int main() {
scanf("%d %d", &n, &m);
vector<int> v[10000];
for(int i = 0; i < m ; i++){
string t1, t2;
cin >> t1 >> t2;
//同性朋友
if(t1.length() == t2.length()){
v[abs(stoi(t1))].emplace_back(abs(stoi(t2)));
v[abs(stoi(t2))].emplace_back(abs(stoi(t1)));
}
//所有朋友
arr[abs(stoi(t1))*10000 + abs(stoi(t2))] = arr[abs(stoi(t2))*10000 + abs(stoi(t1))] = true;
}
scanf("%d", &k);
for(int i = 0 ; i < k ; i++){
int a, b;
cin >> a >> b;
vector<node> ans;
for(int j = 0 ; j < v[abs(a)].size(); j++){
for(int l = 0 ; l < v[abs(b)].size(); l++){
if(v[abs(a)][j] == abs(b) || v[abs(b)][l] == abs(a)) continue;
if(arr[ v[abs(a)][j] * 10000 + v[abs(b)][l] ] == true)
ans.emplace_back(node{v[abs(a)][j], v[abs(b)][l]});
}
}
sort(ans.begin(), ans.end(), cmp);
printf("%d\n", ans.size());
for(int j = 0 ; j < ans.size(); j++)
printf("%04d %04d\n", ans[j].c, ans[j].d);
}
return 0;
}
//1131 寻找最短路径dfs
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
vector<vector<int>> v(10000);
unordered_map<int, int> line;
int visit[10000], minCnt, minTransfer, start, end1;
vector<int> path, tempPath;
int transferCnt(vector<int> a){
int cnt = -1, preLine = 0;
for(int i = 1 ; i < a.size(); i++){
if(line[a[i-1]*10000+a[i]] != preLine) cnt++;
preLine = line[a[i-1]*10000+a[i]];
}
return cnt;
}
void dfs(int node, int cnt){
if(node == end1 && (cnt < minCnt || (cnt == minCnt && transferCnt(tempPath) < minTransfer))){
minCnt = cnt;
minTransfer = transferCnt(tempPath);
path = tempPath;
}
if(node == end1) return;
for(int i = 0 ; i < v[node].size(); i++){
if(visit[v[node][i]] == 0){
visit[v[node][i]] = 1;
tempPath.emplace_back(v[node][i]);
dfs(v[node][i], cnt+1);
visit[v[node][i]] = 0;
tempPath.pop_back();
}
}
}
int main() {
int n, m, k;
scanf("%d", &n);
for(int i = 1 ; i <= n ; i++){
int pre, cur;
scanf("%d %d", &m, &pre);
for(int j = 0; j < m-1; j++){
scanf("%d", &cur);
v[pre].emplace_back(cur);
v[cur].emplace_back(pre);
line[pre*10000+cur] = line[cur*10000+pre] = i;
pre = cur;
}
}
scanf("%d", &k);
for(int i = 0 ; i < k; i++){
scanf("%d %d", &start, &end1);
minCnt = 99999, minTransfer = 99999;
tempPath.clear();
tempPath.emplace_back(start);
visit[start] = 1;
dfs(start, 0);
visit[start] = 0;
printf("%d\n", minCnt);
int preLine = 0, preTransfer = start;
for(int j = 1; j < path.size(); j++){
if(line[path[j-1]*10000+path[j]] != preLine){
if(preLine != 0) printf("Take Line#%d from %04d to %04d.\n", preLine, preTransfer, path[j-1]);
preLine = line[path[j-1]*10000+path[j]];
preTransfer = path[j-1];
}
}
printf("Take Line#%d from %04d to %04d.\n", preLine, preTransfer, end1);
}
return 0;
}
//1111 Dijkstra + DFS 两次Dijkstra分别算路程最短和时间最短 DFS倒输出
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int inf = 999999999;
int dis[510], Time[510], e[510][510], w[510][510], disPre[510], timePre[510], weight[510], nodeNum[510];
bool visit[510];
vector<int> disPath, timePath, tempPath;
// start final
int st, fin, minnode = inf;
int n, m, k;
void dfsdispath(int v){
disPath.emplace_back(v);
if(v == st) return;
dfsdispath(disPre[v]);
}
void dfstimepath(int v){
timePath.emplace_back(v);
if(v == st) return;
dfstimepath(timePre[v]);
}
int main() {
fill(dis, dis + 510, inf);
fill(Time, Time + 510, inf);
fill(weight, weight + 510, inf);
fill(e[0], e[0] + 510 * 510, inf);
fill(w[0], w[0] + 510 * 510, inf);
scanf("%d %d", &n, &m);
int a, b, flag, len, t;
for(int i = 0 ; i < m; i++){
scanf("%d %d %d %d %d", &a, &b, &flag, &len, &t);
e[a][b] = len;
w[a][b] = t;
if(flag != 1){
e[b][a] = len;
w[b][a] = t;
}
}
scanf("%d %d", &st, &fin);
dis[st] = 0;
for(int i = 0; i < n; i++)
disPre[i] = i;
for(int i = 0; i < n; i++){
int u = -1, minn = inf;
for(int j = 0 ; j < n ; j++){
if(visit[j] == false && dis[j] < minn){
u = j;
minn = dis[j];
}
}
if(u == -1) break;
visit[u] = true;
for(int v = 0; v < n ; v++){
if(e[u][v] + dis[u] < dis[v]){
dis[v] = e[u][v] + dis[u];
disPre[v] = u;
weight[v] = weight[u] + w[u][v];
}else if(e[u][v] + dis[u] == dis[v] && weight[v] > weight[u] + w[u][v]){
weight[v] = weight[u] + w[u][v];
disPre[v] = u;
}
}
}
dfsdispath(fin);
Time[st] = 0;
fill(visit, visit+510, false);
for(int i = 0 ; i < n ; i++){
int u = -1, minn = inf;
for(int j = 0 ; j < n ; j++){
if(visit[j]==false && minn > Time[j]){
u = j;
minn = Time[j];
}
}
if(u == -1) break;
visit[u] = true;
for(int v = 0; v < n; v++){
if(visit[v] == false && w[u][v] != inf){
if(w[u][v] + Time[u] < Time[v]){
Time[v] = w[u][v] + Time[u];
timePre[v] = u;
nodeNum[v] = nodeNum[u]+1;
}else if(w[u][v] + Time[u] == Time[v] && nodeNum[u]+1 < nodeNum[v]){
timePre[v] = u;
nodeNum[v] = nodeNum[u]+1;
}
}
}
}
dfstimepath(fin);
printf("Distance = %d", dis[fin]);
if(disPath == timePath){
printf("; Time = %d: ", Time[fin]);
}else{
printf(": ");
for(int i = disPath.size()-1; i >= 0; i--){
printf("%d", disPath[i]);
if(i!=0) printf(" -> ");
}
printf("\nTime = %d: ", Time[fin]);
}
for(int i = timePath.size() - 1; i >= 0; i--){
printf("%d", timePath[i]);
if(i!=0) printf(" -> ");
}
return 0;
}
//1107 并查集 注意研究对象主体和参数关系
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, k, t, cnt = 0;
vector<int> fa, isRoot;
//int fa[1009], cnt[1009] = {0};
//查操作
int find(int a) {
if(a != fa[a])
fa[a] = find(fa[a]);
return fa[a];
}
//并操作
void Union(int a, int b) {
if(find(a) != find(b)) fa[find(a)] = find(b);
}
int cmp(int a, int b){
return a > b;
}
int main() {
int course[1001] = {0};
scanf("%d", &n);
fa.resize(n+1);
isRoot.resize(n+1);
for(int i = 1; i <= n; i++)
fa[i] = i;
for(int i = 1; i <= n ;i++){
scanf("%d:", &k);
for(int j = 0 ; j < k; j++){
scanf("%d", &t);
if(course[t] == 0) course[t] = i;
Union(i, find(course[t]));
}
}
for(int i = 1; i <= n ; i++)
isRoot[find(i)]++;
for(int i = 1; i <= n ; i++)
if(isRoot[i] != 0) cnt++;
printf("%d\n", cnt);
sort(isRoot.begin(), isRoot.end(), cmp);
for(int i = 0 ; i < cnt; i++){
if(i!=0) printf(" ");
printf("%d", isRoot[i]);
}
return 0;
}