重庆交通大学2020级程序设计方法期末考试 题解

A 要好的朋友

题面

【问题描述】

某个班有n名学生,某两个学生可能是“要好”关系。给定n名学生之间的要好关系,输出每个学生有几个要好的朋友。

【输入形式】

输入文件中包含多个测试数据。每个测试数据描述了一个班级,其中第1行为两个整数n和m,n为学生人数,m为“要好”关系数,2≤n≤50,1≤m≤1225,学生序号为1~n;接下来有m行,每行为两个整数u和v,表示学生u和v是要好关系,要好关系不会重复出现,且每个学生和自己不是要好关系。测试数据一直到文件尾。

【输出形式】

对每个测试数据,输出一行,为n个整数,表示第1~n个学生要好的朋友数,每两个整数之间输出一个空格。

【样例输入】

4 4
1 2
1 4
2 3
3 4

【样例输出】

2 2 2 2

题解

题目要求输出每个人朋友的数量,由题可知,朋友数量即为当前节点的出度(与其他节点相连的边的数量)

所以只需要开一个数组num[N],当输入a,b时,即num[a]++,num[b]++,最终遍历输出数组num即可.

也可以使用STL中的vector<int>g[N],对于每个a,b,进行g[a].push_back(b),和g[b].push_back(a);最终遍历g[N].size()即可.


代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int n, m;
const int N = 1010;
vector<int>g[N];
void solve() {
	while (cin >> n >> m) {
		for (int i = 1; i <= n; i++)
			g[i].clear();
		while (m--) {
			int a, b;
			cin >> a >> b;
			g[a].push_back(b);
			g[b].push_back(a);
		}
		for (int i = 1; i <= n; i++) {
			cout << g[i].size() << " ";
		}
		cout << endl;
	}

}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();
}

B 超级完美数

题面

问题描述:

在本题中,如果一个正整数合数的质数因子分解中所有因子之和也能整除该正整数,则该合数被称为超级完美数。例如105 = 3×5×7,质数因子之和为3+5+7=15,15也能整除105,所以105是一个超级完美数。而24 = 2×2×2×3,质数因子之和为2+2+2+3=9,9不能整除24,因此24不是一个超级完美数。

如果质数之和等于正整数本身,这种正整数不算是超级完美数。另外,对质数来说,总是满足“质数因子之和能整除它本身”,所以在本题中,质数不算是超级完美数。(1既不是合数,也不是质数,2是质数)

输入描述:

本题没有输入数据。

输出描述:

输出2~10000之内的每个超级完美数,每个数占一行。

样例输入:

本题无输入数据。

样例输出:

16

27

30

60

70

72

84

题解

如题,我们可以根据题目描述逐步实现程序,但这样有概率会超时,所以需要进行优化.
1:可以注意到题目要求质数不能是完美数,所以我们在遍历1~n时,需要对每个数判定是否为质数.我们可以使用线性筛法在O(n)的时间复杂度内筛出1~n中的所有质数.在判断时即可以O(1)时间复杂度进行判断.
2:在将一个数分解质因数时,我们需要从2枚举到x,判断每个数是否为质数,如果是质数,再对x进行分解.其实可以枚举线性筛法筛出来的质数直接进行分解.   
3:如果没有学过线性筛法,我们注意到输出数据是固定的.所以我们完全可以在本地电脑用暴力算法跑出结果,再copy到代码里输出即可(俗称打表).

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e5 + 10;
int primes[N];
bool st[N];
int cnt;

void get_primes(int n) { // 线性筛质数
	for (int i = 2; i <= n; i ++ ) {
		if (!st[i])
			primes[	cnt ++ ] = i;
		for (int j = 0; primes[j] <= n / i; j ++ ) {
			st[primes[j] * i] = true;
			if (i % primes[j] == 0)
				break;
		}
	}
}

int div(int x) {
	int u = x;
	int res = 0;
	for (int i = 0; i < cnt; i++) {
		if (x < primes[i])
			break;
		while (x % primes[i] == 0) {
			res += primes[i];
			x /= primes[i];
		}
	}//2 2 2 2
	if (!x)
		res += x;
	return res;
}

void solve() {
	get_primes(1e5);
	for (int i = 2; i <= 10000; i++) {
		if (!st[i])
			continue;
		int u = div(i);
		if (i % u == 0 && i != u) {
			cout << i << endl;
		}

	}

}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();
}

C 判定是否可达

问题描述:

给定一个有向图G,以及两个顶点s和t,判定s是否可达t(即判断是否存在从s到t的有向路径)。

输入描述:

输入文件中包含多个测试数据。每个测试数据的第一行为两个整数n和m,分别表示图G的顶点数和边数,顶点序号为1~n。接下来有m行,每行为两个整数u和v,描述了一条有向边<u, v>。接下来一行为两个整数,即s和t,s≠t,且范围在[1,n]。5≤n≤50,m的取值保证每个有向图是简单图且有向图的基图是连通的。

输入文件最后一行为0 0,代表输入数据结束。

(提醒:输入文件中数据量很大)

样例输入中测试数据所描绘的有向图如下图所示。
请添加图片描述

输出描述:

对输入文件中的每个测试数据,如果s可达t,输出yes,否则输出no。

样例输入:样例输出:

7 9 yes

1 2

2 3

2 5

2 6

3 5

4 3

5 2

5 4

6 7

3 6

0 0

题解

经典的dfs问题,建完图后只需从s点dfs即可。

(某瞎子没看到是有向边怒wa一发,wa后死性不改写并查集又wa一发)

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;

int n, m;
const int N = 1010;
vector<int>g[N];
bool st[N];
int s, t;
int ok = 0;
void dfs(int u, int fa) {
	if (u == t) {
		ok = 1;
		return;
	}
	for (auto i : g[u]) {
		if (i == fa)
			continue;
		if (st[i])
			continue;
		st[i] = true;
		dfs(i, u);
	}
}

void solve() {
	while (cin >> n >> m) {
		if (n == 0)
			break;
		for (int i = 1; i <= n; i++) {
			g[i].clear();
			st[i] = false;
		}
		while (m--) {
			int a, b;
			cin >> a >> b;
			g[a].push_back(b);
		}
		ok = 0;
		cin >> s >> t;
		dfs(s, -1);
		if (ok) {
			cout << "yes" << endl;
		} else {
			cout << "no" << endl;
		}
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();
}

D 分数

题面

【问题描述】

考虑以下集合:

S = { p/q | w <= p <= x, y <= q <= z }。
即,S是满足以下条件的分数的集合:分子介于w和x之间(含w和x)、分母介于y和z之间(含y和z)。注意,如果p1/q1和p2/q2化简后值相同,则在S集合里是同一个分数。
给定w、x、y和z,求S集合中不同元素的个数。
注意:
\1. p、q、w、x、y和z均为整数。
\2. 1≤x, z≤100。
\3. 1≤w≤x。
\4. 1≤y≤z。

【输入形式】

输入文件中包含多个测试数据。每个测试数据占一行,为4个整数w、x、y和z。输入文件最后一行为0 0 0 0,表示输入结束。

【输出形式】

对输入文件中的每个测试数据,输出求得的S集合中不同元素的个数。

【样例输入】

1 1 1 1
1 10 1 1
1 2 1 2
2 4 2 4
1 100 1 100
0 0 0 0

【样例输出】

1
10
3
7
6087

题解

一般情况下会老老实实地开个pair,老老实实地化简,比较,去重……多累!

来试试神奇的set<double>

代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int w, x, y, z;
set<double>s;

void solve() {
	while (cin >> w >> x >> y >> z) {
		s.clear();
		if (w == 0 && x == 0 && y == 0 && z == 0) {
			break;
		}
		for (int i = w; i <= x; i++) {
			for (int j = y; j <= z; j++) {
				double p = (i / 1.0) / (j / 1.0);
				s.insert(p);
			}
		}
		cout << s.size() << endl;
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	solve();
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值