C/C++编程(1~8级)全部真题・点这里
第1题:最短路径问题
平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。
若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。现在的任务是找出从一点到另一点之间的最短路径。
时间限制:1000
内存限制:131072
输入
共n+m+3行,其中: 第一行为整数n。 第2行到第n+1行(共n行) ,每行两个整数x和y,描述了一个点的坐标。 第n+2行为一个整数m,表示图中连线的个数。 此后的m 行,每行描述一条连线,由两个整数i和j组成,表示第i个点和第j个点之间有连线。 最后一行:两个整数s和t,分别表示源点和目标点。
输出
仅一行,一个实数(保留两位小数),表示从s到t的最短路径长度。
样例输入
5
0 0
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5
样例输出
3.41
下面是一个使用C语言解决最短路径问题的示例代码:
#include <stdio.h>
#include <math.h>
#define MAX_POINTS 100
typedef struct {
int x;
int y;
} Point;
double distance(Point p1, Point p2) {
int dx = p2.x - p1.x;
int dy = p2.y - p1.y;
return sqrt(dx * dx + dy * dy);
}
double shortestPath(Point points[], int n, int connections[][2], int m, int source, int target) {
double dist[MAX_POINTS];
int visited[MAX_POINTS] = {0};
int i, j;
for (i = 0; i < n; i++) {
dist[i] = INFINITY;
}
dist[source] = 0;
for (i = 0; i < n - 1; i++) {
int minIndex = -1;
double minDist = INFINITY;
for (j = 0; j < n; j++) {
if (!visited[j] && dist[j] < minDist) {
minIndex = j;
minDist = dist[j];
}
}
visited[minIndex] = 1;
for (j = 0; j < m; j++) {
int u = connections[j][0];
int v = connections[j][1];
double d = distance(points[u], points[v]);
if (!visited[v] && dist[u] + d < dist[v]) {
dist[v] = dist[u] + d;
}
}
}
return dist[target];
}
int main() {
int n, m;
Point points[MAX_POINTS];
int connections[MAX_POINTS][2];
int source, target;
int i;
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d %d", &points[i].x, &points[i].y);
}
scanf("%d", &m);
for (i = 0; i < m; i++) {
scanf("%d %d", &connections[i][0], &connections[i][1]);
}
scanf("%d %d", &source, &target);
double shortestDist = shortestPath(points, n, connections, m, source - 1, target - 1);
printf("%.2f\n", shortestDist);
return 0;
}
解释:
首先,我们定义了一个Point
结构体来表示点的坐标。
distance()
函数用于计算两个点之间的直线距离。
shortestPath()
函数实现了Dijkstra算法来找到最短路径的长度。它使用一个距离数组dist
来保存每个点到源点的最短距离,visited
数组用于标记已经访问过的点。在每一轮迭代中,它选择距离源点最近且未访问过的点作为当前点,然后更新与当前点相邻的点的最短距离。
在main()
函数中,我们首先读取输入数据,包括点的坐标、连线的信息以及源点和目标点的编号。
然后调用shortestPath()
函数来计算最短路径的长度,并打印结果。
第2题:控制公司
有些公司是其他公司的部分拥有者,因为他们获得了其他公司发行的股票的一部分。例如,福特公司拥有马自达公司12%的股票。据说,如果至少满足了以下条件之一,公司A就可以控制公司B了:
l 公司A = 公司B。
l 公司A拥有大于50%的公司B的股票。
l 公司A控制K(K >= 1)个公司,记为C1, …, CK,每个公司Ci拥有xi%的公司B的股票,并且x1+ … + xK > 50%。(ps:A可以控制自己,即Ci可以为A)
你将被给予一系列的三对数(i,j,p),表明公司i拥有公司j的p%的股票。计算所有的数对(h,s),表明公司h控制公司s。
写一个程序读入三对数(i,j,p),并且找出所有的数对(h,s),使得公司h控制公司s。
时间限制:1000
内存限制:65536
输入
第一行: N,表明接下来三对数的数量。 第二行到第N+1行:每行三个整数作为一个三对数(i,j,p),如上文所述。 I,J≤100,N,P≤100
输出
输出零个或更多个的控制其他公司的公司。每行包括两个整数表明序号为第一个整数的公司控制了序号为第二个整数的公司。将输出的每行以第一个数字升序排列(并且第二个数字也升序排列来避免并列)。请不要输出控制自己的公司。
样例输入
3
1 2 80
2 3 80
3 1 20
样例输出
1 2
1 3
2 3
下面是一个使用C语言解决控制公司问题的示例代码:
#include <stdio.h>
#define MAX_COMPANIES 100
int ownership[MAX_COMPANIES][MAX_COMPANIES] = {0};
int controlled[MAX_COMPANIES][MAX_COMPANIES] = {0};
int n;
void dfs(int company, int owner) {
int i;
for (i = 0; i < n; i++) {
if (ownership[company][i] > 0 && controlled[owner][i] == 0) {
controlled[owner][i] = 1;
dfs(i, owner);
}
}
}
int main() {
scanf("%d", &n);
int i, j, p;
while (n--) {
scanf("%d %d %d", &i, &j, &p);
ownership[i - 1][j - 1] = p;
}
for (i = 0; i < MAX_COMPANIES; i++) {
controlled[i][i] = 1;
for (j = 0; j < MAX_COMPANIES; j++) {
if (ownership[i][j] > 50) {
controlled[i][j] = 1;
dfs(j, i);
}
}
}
for (i = 0; i < MAX_COMPANIES; i++) {
for (j = 0; j < MAX_COMPANIES; j++) {
if (controlled[i][j] && i != j) {
printf("%d %d\n", i + 1, j + 1);
}
}
}
return 0;
}
解释:
首先,我们定义了两个二维数组ownership
和controlled
,用于存储公司之间的股权关系和控制关系。
dfs()
函数是一个深度优先搜索算法,用于遍历公司之间的股权关系,并更新控制关系。
在main()
函数中,我们首先从输入中读取公司之间的股权关系,并将其保存在ownership
数组中。
然后,使用两个嵌套循环遍历所有公司,如果某个公司拥有超过50%的另一个公司的股权,则将其标记为控制关系,并调用dfs()
函数来更新其他受控公司。
最后,我们遍历controlled
数组,输出所有的控制关系。
第3题:发现它,抓住它
一个城市中有两个犯罪团伙A和B,你需要帮助警察判断任意两起案件是否是同一个犯罪团伙所为,警察所获得的信息是有限的。假设现在有N起案件(N<=100000),编号为1到N,每起案件由团伙A或团伙B所为。你将按时间顺序获得M条信息(M<=100000),这些信息分为两类:
1、D [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息表明它们属于不同的团伙所为
2、A [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息需要你回答[a]和[b]是否是同一个团伙所为
注意你获得信息的时间是有先后顺序的,在回答的时候只能根据已经接收到的信息做出判断。
时间限制:1000
内存限制:65536
输入
第一行是测试数据的数量T(1<=T<=20)。 每组测试数据的第一行包括两个数N和M,分别表示案件的数量和信息的数量,其后M行表示按时间顺序收到的M条信息。
输出
对于每条需要回答的信息,你需要输出一行答案。如果是同一个团伙所为,回答"In the same gang.“,如果不是,回答"In different gangs.”,如果不确定,回答”Not sure yet."。
样例输入
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
样例输出
Not sure yet.
In different gangs.
In the same gang.
下面是一个使用C语言解决判断案件团伙问题的示例代码:
#include <stdio.h>
#define MAX_CASES 100000
int gang[MAX_CASES];
int find(int x) {
if (gang[x] == x)
return x;
return gang[x] = find(gang[x]);
}
void merge(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx != fy)
gang[fy] = fx;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int N, M;
scanf("%d %d", &N, &M);
// 初始化每个案件所属的团伙
for (int i = 1; i <= N; i++)
gang[i] = i;
char op;
int a, b;
while (M--) {
scanf(" %c %d %d", &op, &a, &b);
if (op == 'A') {
// 判断是否属于同一个团伙
if (find(a) == find(b))
printf("In the same gang.\n");
else
printf("In different gangs.\n");
} else if (op == 'D') {
// 合并两个团伙
merge(a, b);
}
}
}
return 0;
}
解释:
首先,我们定义了一个整数数组gang
,用于存储每个案件所属的团伙。
find()
函数是一个递归的查找函数,用于找到某个案件所属的团伙的根节点。
merge()
函数用于合并两个团伙,将其中一个团伙的根节点指向另一个团伙的根节点。
在main()
函数中,我们首先读取测试数据的数量T
,然后开始处理每组测试数据。
对于每组测试数据,我们首先根据案件数量N
初始化每个案件所属的团伙为自身。
然后,依次读取每条信息,并根据信息的类型进行相应的操作。如果是类型为A
的信息,我们调用find()
函数判断两个案件是否属于同一个团伙;如果是类型为D
的信息,我们调用merge()
函数合并两个团伙。
最后,根据判断的结果输出相应的答案。
第4题:最短路
给定一个n个点, m条边的有向图, 求从点S出发, 到其它所有点的最短路径.
时间限制:2000
内存限制:65536
输入
第一行一个整数T, 表示有T组数据 对于每组测试数据, 第一行三个整数n, m, S, 表示有n个点, m条边, 起点为S. 接下来m行, 每行三个整数x, y, z, 代表从x到y有长度为z的边 点的编号从1到n T <= 10, n <= 10000, m <= 20000, |z| <= 10000. 所有数据的n之和 <= 30000, 所有数据的m之和 <= 60000.
输出
对于每组数据: 如果从S点出发可以走入负圈 (即到某些点的最短路径可以无限小), 那么输出一行Error. 否则, 输出一行用空格分隔的n个整数, 其中第i个整数表示从S点到i点的最短路长度. 如果从S点无法到达i点, 则第i个输出为”null”.
样例输入
4
5 7 1
1 2 3
2 3 4
3 4 8
1 3 9
4 5 1
1 4 5
1 5 10
4 4 1
1 2 -4
2 3 8
1 3 5
3 4 0
3 3 2
1 2 -3
2 3 -4
3 1 6
4 2 1
1 2 1
3 4 2
样例输出
0 3 7 5 6
0 -4 4 4
Error
0 1 null null
下面是一个使用C语言解决最短路径问题的示例代码,使用Dijkstra算法:
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#define MAX_NODES 10001
#define MAX_EDGES 60001
#define INF INT_MAX
typedef struct {
int node;
int cost;
} Edge;
typedef struct {
Edge edges[MAX_EDGES];
int count;
} AdjList;
AdjList graph[MAX_NODES];
int dist[MAX_NODES];
void dijkstra(int start, int n) {
bool visited[MAX_NODES];
int i, j;
for (i = 1; i <= n; i++) {
dist[i] = INF;
visited[i] = false;
}
dist[start] = 0;
for (i = 1; i <= n; i++) {
int u = -1;
for (j = 1; j <= n; j++) {
if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
u = j;
}
}
if (dist[u] == INF) {
break;
}
visited[u] = true;
for (j = 0; j < graph[u].count; j++) {
int v = graph[u].edges[j].node;
int cost = graph[u].edges[j].cost;
if (dist[u] + cost < dist[v]) {
dist[v] = dist[u] + cost;
}
}
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m, S;
scanf("%d %d %d", &n, &m, &S);
int i;
for (i = 1; i <= n; i++) {
graph[i].count = 0;
}
for (i = 0; i < m; i++) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
graph[x].edges[graph[x].count].node = y;
graph[x].edges[graph[x].count].cost = z;
graph[x].count++;
}
dijkstra(S, n);
for (i = 1; i <= n; i++) {
if (dist[i] == INF) {
printf("null ");
} else {
printf("%d ", dist[i]);
}
}
printf("\n");
}
return 0;
}
解释:
首先,我们定义了一个结构体Edge
,用于表示图中的边,包括目标节点和边的权重。
然后,我们定义了一个结构体AdjList
,用于表示邻接表,其中包含一个Edge
类型的数组以及边的数量。
我们使用邻接表数组graph
来表示图,每个节点对应一个邻接表。
dijkstra()
函数是一个实现Dijkstra算法的函数,用于计算从起点出发到其他所有点的最短路径。
在main()
函数中,我们首先读取测试数据的数量T
,然后开始处理每组测试数据。
对于每组测试数据,我们首先根据节点数量n
初始化图的邻接表。
然后,依次读取每条边的信息,并将其添加到对应节点的邻接表中。
接下来,我们调用dijkstra()
函数计算从起点S
出发到其他所有点的最短路径。
最后,我们根据最短路径的计算结果输出每个节点的最短路径长度或"null"。