先来一道热身题,蓝桥杯 算法提高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;
}