PAT/C++甲级题解——图

第四章:图、堆、并查集

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;

②常用函数
  1. sort( ) bool cmp()
③常用方法
  1. 【注意点】同组图重复判断不同案例时记得重置初始数据

    //初始
    vector<int> v[1009];
    //输入完毕
    ……
    //重复利用判断
    vector<int> in(v, v+n+1);
    
  2. 【注意点】多种情况利用if及else if 不要一直用if else

  3. 是否为连通图的判断

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 时 为连通图
  1. 【注意点】哈密顿图及其变体类题型不能形成交叉环

    //注意判断 k1 == n + 1
    //  是否断路	是否遍历所有顶点	是否头尾相同成环	是否多环
    if(flag && se.size() == n && lst[0] == lst[1] && k1 == n + 1)
    
  2. 【注意点】并查集 查操作与并操作

    ​ 并查集大统计注意定义数据结构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;
    }
    
  3. 深度优先遍历找根到叶的所有路径

    // 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();
    	}
    }
    
    
  4. 最短路径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;
    			}*/
    		}
    	}
    }
    
    
  5. 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;//判断为前驱
                    }
                }
            }
        }
    }
    
  6. 并查集找主要联系 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值