强连通分量 floyd算法 每日练习

先来一道热身题,蓝桥杯 算法提高ADV-284 GPA
1.问题描述
  输入A,B两人的学分获取情况,输出两人GPA之差。
输入格式
  输入的第一行包含一个整数n表示A的课程数,以下n行每行Si,Ci分别表示第i个课程的学分与A的表现。
  GPA=Σ(Si*Ci) / Σ(Si)。
  特殊地,如果Ci是’P’或者’N’(对应于通过与不通过),则第i个课程不记入GPA的计算(即当其不存在)。
  A读入结束后读入B,B的输入格式与A相同。
  保证2人的Σ(Si)非零
输出格式
  输出A的GPA - B的GPA的值,保留2位小数(四舍五入)
  Tips:当A和B的分数相近时输出0.00。
 代码如下,注意A和B的分数差小于0.01的时候就要输出零

#include<iostream>
#include<string>
#include<cmath>
using namespace std;

int main() {
	int n;			//学科数目 
	float Sa=0, Sb=0;			//学分 
	float Ca = 0, Cb = 0;		//学分绩点 
	char c[5];
	int s;
	float result_a, result_b;

	scanf("%d", &n);
	for(int i=0;i<n;i++){		//计算Sa、Ca
		scanf("%d", &s);
		scanf("%s", c);
		if (c[0] != 'P'&&c[0] != 'N') {		//如果是P或者N,就不计入
			Sa += s;
			int tmp = 0;
			for (int i = 0; ; i++) {		//将字符串转换为十进制数字
				if (c[i] == '\0')
					break;
				tmp = tmp * 10 + c[i] - 48;
			}
			Ca += tmp * s;
		}
	}

	scanf("%d", &n);		//计算Sb、Cb
	for (int i = 0; i < n; i++) {
		scanf("%d", &s);
		scanf("%s", &c[0]);
		if (c[0] != 'P'&&c[0] != 'N') {
			Sb += s;
			int tmp = 0;
			for (int i = 0; ; i++) {
				if (c[i] == '\0')
					break;
				tmp = tmp * 10 + c[i] - 48;
			}
			Cb += tmp * s;
		}
	}
	result_a = Ca / Sa;		//printf输出的时候默认四舍五入
	result_b =Cb / Sb;
	if (result_a - result_b<=0.01&& result_a - result_b >= -0.01)	//这里必须这样写,否则一个测试用例过不了,无须在意这里
		printf("0.00");
	else
		printf("%.2f",result_a - result_b);
	return 0;
}

一开始我用的string来替代char c[],但是运行的时候出错了,原因是用string的时候,如果用cin读入cout输出,没有问题,如果用scanf读入,必须这样:

	scanf("%s",&c[0]);

输出的时候如果用printf,要这样:

	printf("%s", c.c_str());

2.Kosaraju算法
该算法用来求有向图的强连通分量,基本思想是做两次dfs,第一次正向dfs,然后按照递归回退的顺序记录顶点(越后优先级越高),然后取优先级最高的顶点u,作为起点,反向dfs,因为原本与u同处一个强连通分量的顶点,就算边方向了,也还是能访问到,而那些与u不属于一个强连通分量的顶点,因为第一次dfs回退的顺序就是该图拓扑排序的一个逆序,每次取优先级最高的顶点,就是取图的”开始部分“,因为边都反向了,故不可能遍历到和其不是一个强连通分量的顶点。继续取优先级最高的顶点,继续上述操作,直到遍历所有顶点。Kosaraju算法的复杂度是O(V+E)。
下面以hdu1269 迷宫城堡 为例,给出代码:

//hdu 1269 迷宫城堡
//一个有向图,有n-1个点(n<=10000)和m条边(m<=100000)。判断整个图是否强连通,如果是,输出Yes,否则输出No。 
#include<stdio.h>
#include<vector>
#include<cstring>
using namespace std;

const int NUM = 10005;
vector<int> G[NUM], rG[NUM];
vector<int> S;			//存第一次dfs1()的结果,并标记点的先后顺序
int vis[NUM], sccno[NUM], cnt;		//cnt是强连通分量的个数

void dfs1(int u) {
	if (vis[u])
		return;
	vis[u] = 1;
	for (int i = 0; i < G[u].size(); i++)
		dfs1(G[u][i]);
	S.push_back(u);			//记录点的先后顺序,标记大的放在S的后面
}
void dfs2(int u) {
	if (sccno[u])
		return;
	sccno[u] = cnt;
	for (int i = 0; i < rG[u].size(); i++)
		dfs2(rG[u][i]);
}
void Kosaraju(int n) {
	cnt = 0;
	S.clear();
	memset(sccno, 0, sizeof(sccno));
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i <= n; i++)
		dfs1(i);				//点的编号:1~n 递归所有点
	for (int i = n - 1; i >= 0; i--)
		if (!sccno[S[i]]) {
			cnt++;
			dfs2(S[i]);
		}
}

int main() {
	int n, m, u, v;
	while (scanf("%d%d", &n, &m), n != 0 || m != 0) {
		for (int i = 1; i <= n; i++) {
			G[i].clear();
			rG[i].clear();
		}
		for (int i = 0; i < m; i++) {
			scanf("%d%d", &u, &v);
			G[u].push_back(v);			//原图
			rG[v].push_back(u);			//反图

		}
		Kosaraju(n);
		printf("%s\n", cnt == 1 ? "Yes" : "No");
	}
	return 0;
}

3.floyd算法
floyd算法可以一次求出所有结点间的最短路径,而且程序很简单。其缺点就是效率不高,复杂度达到了O(n^3),只能在n<200这样的小规模情况下使用。
floyd用到了动态规划的思想:求两点i、j之间的最短距离,可以分为两种情况,即经过图中某个点k的路径和不经过点k的路径,取两者中的最短路径。

floyd算法 
#include<stdio.h>
using namespace std;

const int INF=1e6;
const int NUM=105;
int graph[NUM][NUM];
int n,m;

void floyd(){
	int s=1;
	for(int k=1;k<=n;k++)			//遍历每个中转站
		for(int i=1;i<=n;i++)		//i和j用来遍历每个路径
			if(graph[i][k]!=INF)
				for(int j=1;j<=n;j++)
					if(graph[i][j]>graph[i][k]+graph[k][j])
							graph[i][j]=graph[i][k]+graph[k][j];
}

int main(){
	while(~scanf("%d%d",&n,&m)){
		if(n==0&&m==0)
			return 0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)		//初始化
				if(i==j)
					graph[i][j]=0;
				else
					graph[i][j]=INF;
					
		while(m--){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);	
			graph[a][b]=graph[b][a]=c;
		}
		floyd();	
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++)
				printf("%3d",graph[i][j]);
			printf("\n");
		}
	}
	return 0;
}

关于floyd算法,这里有个很好的文章,讲解很透彻,生动精辟

发布了17 篇原创文章 · 获赞 2 · 访问量 437

猜你喜欢

转载自blog.csdn.net/Raymond_YP/article/details/104286892
今日推荐