1-1 采用邻接矩阵表示法创建无向图
分数 4
作者 王东
单位 贵州师范学院
采用邻接矩阵表示法创建无向图G ,依次输出各顶点的度。
函数接口定义:
void CreateUDN (AMGraph &G) ;
其中 G 是采用邻接矩阵表示的无向图。
裁判测试程序样例:
#include <stdio.h> #define MVNum 100 typedef struct { char vexs[MVNum]; int arcs[MVNum][MVNum]; int vexnum,arcnum; }AMGraph; void CreateUDN (AMGraph &G) ;int main () { AMGraph G; int i , j,sum=0 ; CreateUDN (G); for (i = 0 ; i < G.vexnum ; ++i){ sum=0 ; for (j = 0 ; j < G.vexnum; ++j){ if (G.arcs[i][j]==1 ) sum+=1 ; } if (i==0 ) printf ("%d" ,sum); else printf (" %d" ,sum); } return 0 ; }
输入格式:
输入第一行中给出2个整数i(0<i≤10),j(j≥0),分别为图G的顶点数和边数。
输入第二行为顶点的信息,每个顶点只能用一个字符表示。
依次输入j行,每行输入一条边依附的顶点。
输出格式:
依次输出各顶点的度,行末没有最后的空格。
输入样例:
输出样例:
解析
void CreateUDN (AMGraph &G) { scanf ("%d %d" , &G.vexnum, &G.arcnum); scanf ("%s" , G.vexs); for (int i = 0 ; i < G.vexnum; ++i) { for (int j = 0 ; j < G.vexnum; ++j) { G.arcs[i][j] = 0 ; } } char u, v; for (int k = 0 ; k < G.arcnum; ++k) { scanf (" %c%c" , &u, &v); int i = -1 , j = -1 ; for (int m = 0 ; m < G.vexnum; ++m) { if (G.vexs[m] == u) i = m; if (G.vexs[m] == v) j = m; if (i != -1 && j != -1 ) break ; } G.arcs[i][j] = 1 ; G.arcs[j][i] = 1 ; } }
1-2 采用邻接表创建无向图
分数 4
作者 王东
单位 贵州师范学院
采用邻接表创建无向图G ,依次输出各顶点的度。
函数接口定义:
void CreateUDG (ALGraph &G) ;
其中 G 是采用邻接表表示的无向图。
裁判测试程序样例:
#include <stdio.h> #include <stdlib.h> #define MVNum 100 typedef struct ArcNode { int adjvex; struct ArcNode *nextarc; int info; }ArcNode; typedef struct VNode { char data; ArcNode *firstarc; }VNode, AdjList[MVNum]; typedef struct { VNode vertices[MVNum]; int vexnum, arcnum; }ALGraph; void CreateUDG (ALGraph &G) ;int main () { ALGraph G; int i , j,sum=0 ; CreateUDG (G); ArcNode * p; for (i = 0 ; i < G.vexnum ; ++i){ sum=0 ; p=G.vertices[i].firstarc; for (; p!=NULL ; p=p->nextarc){ sum+=1 ; } if (i==0 ) printf ("%d" ,sum); else printf (" %d" ,sum); } return 0 ; }
输入格式:
输入第一行中给出2个整数i(0<i≤10),j(j≥0),分别为图G的顶点数和边数。
输入第二行为顶点的信息,每个顶点只能用一个字符表示。
依次输入j行,每行输入一条边依附的顶点。
输出格式:
依次输出各顶点的度,行末没有最后的空格。
输入样例:
输出样例:
解析
void CreateUDG (ALGraph &G) { scanf ("%d %d" , &G.vexnum, &G.arcnum); getchar (); char vex_str[MVNum]; scanf ("%s" , vex_str); for (int k = 0 ; k < G.vexnum; ++k) { G.vertices[k].data = vex_str[k]; G.vertices[k].firstarc = NULL ; } char u, v; for (int k = 0 ; k < G.arcnum; ++k) { scanf (" %c%c" , &u, &v); int i = -1 , j = -1 ; for (int m = 0 ; m < G.vexnum; ++m) { if (G.vertices[m].data == u) i = m; if (G.vertices[m].data == v) j = m; if (i != -1 && j != -1 ) break ; } if (i == -1 || j == -1 ) continue ; ArcNode *p1 = (ArcNode *)malloc (sizeof (ArcNode)); if (p1 == NULL ) exit (1 ); p1->adjvex = j; p1->nextarc = G.vertices[i].firstarc; G.vertices[i].firstarc = p1; ArcNode *p2 = (ArcNode *)malloc (sizeof (ArcNode)); if (p2 == NULL ) exit (1 ); p2->adjvex = i; p2->nextarc = G.vertices[j].firstarc; G.vertices[j].firstarc = p2; } }
1-3 基于邻接矩阵表示的广度优先遍历
分数 4
作者 王东
单位 贵州师范学院
实现基于邻接矩阵表示的广度优先遍历。
函数接口定义:
void BFS (Graph G, int v) ;
其中 G 是基于邻接矩阵存储表示的无向图,v表示遍历起点。
裁判测试程序样例:
#include <stdio.h> #include <stdlib.h> #define MVNum 10 int visited[MVNum];typedef struct { char vexs[MVNum]; int arcs[MVNum][MVNum]; int vexnum,arcnum; }Graph; void CreateUDN (Graph &G) ;void BFS (Graph G, int v) ;void BFSTraverse (Graph G) { int v; for (v = 0 ; v < G.vexnum; ++v) visited[v] = 0 ; for (v = 0 ; v < G.vexnum; ++v) if (!visited[v]) BFS (G, v); } int main () { Graph G; CreateUDN (G); BFSTraverse (G); return 0 ; }
输入样例:
输入第1行为结点数vexnum和边数arcnum。第2行为结点的值,依次输入arcnum行,每行输入两个结点的值表示该两个结点互为邻接点。
8 8 ABCDEFGH A B A C B D B E C F C G D H E H
输出样例:
遍历序列。
解析
void BFS (Graph G, int v) { int queue[MVNum]; int front = 0 , rear = 0 ; visited[v] = 1 ; printf ("%c " , G.vexs[v]); queue[rear++] = v; while (front < rear) { int u = queue[front++]; for (int w = 0 ; w < G.vexnum; ++w) { if (G.arcs[u][w] == 1 && visited[w] == 0 ) { visited[w] = 1 ; printf ("%c " , G.vexs[w]); queue[rear++] = w; } } } }
1-4 基于邻接表表示的广度优先遍历
分数 4
作者 王东
单位 贵州师范学院
实现基于邻接表表示的广度优先遍历。
函数接口定义:
void BFS (ALGraph G, int v) ;
其中 G 是基于邻接表存储表示的无向图,v表示遍历起点。
裁判测试程序样例:
#include <stdio.h> #include <stdlib.h> #define MVNum 10 #define MAXQSIZE 100 bool visited[MVNum]; typedef struct ArcNode { int adjvex; struct ArcNode *nextarc; int info; }ArcNode; typedef struct VNode { char data; ArcNode *firstarc; }VNode, AdjList[MVNum]; typedef struct { AdjList vertices; int vexnum, arcnum; }ALGraph; int CreateUDG (ALGraph &G) ;void BFS (ALGraph G, int v) ;void BFSTraverse (ALGraph G) { int v; for (v = 0 ; v < G.vexnum; ++v) visited[v] = 0 ; for (v = 0 ; v < G.vexnum; ++v) if (!visited[v]) BFS (G, v); } int main () { ALGraph G; CreateUDG (G); BFSTraverse (G); return 0 ; }
输入样例:
输入第1行为结点数vexnum和边数arcnum。第2行为结点的值,依次输入arcnum行,每行输入两个结点的值表示该两个结点互为邻接点。
8 8 ABCDEFGH A B A C B D B E C F C G D H E H
输出样例:
遍历序列。
解析
void BFS (ALGraph G, int v) { int queue[MAXQSIZE]; int front = 0 , rear = 0 ; visited[v] = true ; printf ("%c " , G.vertices[v].data); queue[rear++] = v; while (front < rear) { int u = queue[front++]; ArcNode *p = G.vertices[u].firstarc; while (p != NULL ) { int w = p->adjvex; if (!visited[w]) { visited[w] = true ; printf ("%c " , G.vertices[w].data); queue[rear++] = w; } p = p->nextarc; } } }
1-5 实现基于邻接表表示的深度优先遍历
分数 4
作者 王东
单位 贵州师范学院
实现基于邻接表表示的深度优先遍历。
函数接口定义:
void DFS (ALGraph G, int v) ;
其中 G 是基于邻接表存储表示的无向图,v表示遍历起点。
裁判测试程序样例:
#include <stdio.h> #include <stdlib.h> #define MVNum 10 int visited[MVNum]; typedef struct ArcNode { int adjvex; struct ArcNode *nextarc; int info; }ArcNode; typedef struct VNode { char data; ArcNode *firstarc; }VNode, AdjList[MVNum]; typedef struct { AdjList vertices; int vexnum, arcnum; }ALGraph; int CreateUDG (ALGraph &G) ;void DFS (ALGraph G, int v) ;void DFSTraverse (ALGraph G) { int v; for (v = 0 ; v < G.vexnum; ++v) visited[v] = 0 ; for (v = 0 ; v < G.vexnum; ++v) if (!visited[v]) DFS (G, v); } int main () { ALGraph G; CreateUDG (G); DFSTraverse (G); return 0 ; }
输入样例:
输入第1行为结点数vexnum和边数arcnum。第2行为结点的值,依次输入arcnum行,每行输入两个结点的值表示该两个结点互为邻接点。
8 8 ABCDEFGH A B A C B D B E C F C G D H E H
输出样例:
遍历序列。
解析
void DFS (ALGraph G, int v) { visited[v] = 1 ; printf ("%c " , G.vertices[v].data); ArcNode *p = G.vertices[v].firstarc; while (p != NULL ) { int w = p->adjvex; if (!visited[w]) { DFS (G, w); } p = p->nextarc; } }
1-6 实现基于邻接矩阵表示的深度优先遍历
分数 4
作者 王东
单位 贵州师范学院
实现基于邻接矩阵表示的深度优先遍历。
函数接口定义:
void DFS (Graph G, int v) ;
其中 G 是基于邻接矩阵存储表示的无向图,v表示遍历起点。。
裁判测试程序样例:
#include <stdio.h> #include <stdlib.h> #define MVNum 10 int visited[MVNum];typedef struct { char vexs[MVNum]; int arcs[MVNum][MVNum]; int vexnum,arcnum; }Graph; void CreateUDN (Graph &G) ;void DFS (Graph G, int v) ;void DFSTraverse (Graph G) { int v; for (v = 0 ; v < G.vexnum; ++v) visited[v] = 0 ; for (v = 0 ; v < G.vexnum; ++v) if (!visited[v]) DFS (G, v); } int main () { Graph G; CreateUDN (G); DFSTraverse (G); return 0 ; }
输入样例:
输入第1行为结点数vexnum和边数arcnum。第2行为结点的值,依次输入arcnum行,每行输入两个结点的值表示该两个结点互为邻接点。
8 8 ABCDEFGH A B A C B D B E C F C G D H E H
输出样例:
遍历序列。
解析
void DFS (Graph G, int v) { printf ("%c " , G.vexs[v]); visited[v] = 1 ; for (int u = 0 ; u < G.vexnum; ++u) { if (!visited[u] && G.arcs[v][u] == 1 ) { DFS (G, u); } } }
1-7 最小生成树(普里姆算法)
分数 4
作者 王东
单位 贵州师范学院
试实现普里姆最小生成树算法。
函数接口定义:
void Prim (AMGraph G, char u) ;
其中 G 是基于邻接矩阵存储表示的无向图,u表示起点
裁判测试程序样例:
#include <iostream> #define MVNum 10 #define MaxInt 32767 using namespace std;struct edge { char adjvex; int lowcost; }closedge[MVNum]; typedef struct { char vexs[MVNum]; int arcs[MVNum][MVNum]; int vexnum,arcnum; }AMGraph; int LocateVex (AMGraph G , char v) ;int Min (AMGraph G) ;int CreateUDN (AMGraph &G) ;void Prim (AMGraph G, char u) ;int main () { AMGraph G; CreateUDN (G); char u; cin >> u; Prim (G , u); return 0 ; }
输入样例:
第1行输入结点数vexnum和边数arcnum。第2行输入vexnum个字符表示结点的值,接下来依次输入arcnum行,每行输入3个值,前两个字符表示结点,后一个数表示两个结点之间边的权值。最后一行输入一个字符表示最小生成树的起始结点。
7 9 0123456 0 1 28 0 5 10 1 2 16 1 6 14 2 3 12 3 6 18 3 4 22 4 5 25 4 6 24 0
输出样例:
按最小生成树的生成顺序输出每条边。
解析
void Prim (AMGraph G, char u) { int v0 = LocateVex (G, u); for (int i = 0 ; i < G.vexnum; ++i) { if (i != v0) { closedge[i].adjvex = u; closedge[i].lowcost = G.arcs[v0][i]; } else { closedge[i].lowcost = 0 ; } } for (int i = 1 ; i < G.vexnum; ++i) { int k = Min (G); cout << closedge[k].adjvex << "->" << G.vexs[k] << endl; closedge[k].lowcost = 0 ; for (int j = 0 ; j < G.vexnum; ++j) { if (closedge[j].lowcost != 0 && G.arcs[k][j] < closedge[j].lowcost) { closedge[j].lowcost = G.arcs[k][j]; closedge[j].adjvex = G.vexs[k]; } } } }
1-8 拓扑排序
分数 4
作者 YJ
单位 西南石油大学
一项工程由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其他子任务后才能执行。例如,下图表示了一项工程若干子任务之间的先后关系。
编写函数输出所有子任务的拓扑序列。
函数接口定义:
Status Push_SeqStack (SeqStack &s, ElemType x) void topsort ( ALGraph &G) { int i,v,w; int cnt=0 ; EdgeNode *ptr; SeqStack st; InitStack_Sq (st); for (i=0 ;i<G.n;i++) { if (___________________) Push_SeqStack (st,i); } while (!Empty_Sq (st)) { Pop_SeqStack ( st, v); printf ("%s " ,G.adjlist[v].vertex); ____________; ptr=G.adjlist[v].firstedge; while (ptr!=NULL ) { w=ptr->adjvex; __________________________; if (G.adjlist[w].Indegree==0 ) ____________________; ptr=ptr->next; } } if (cnt<G.n) printf ("后续无法输出!\n" ); }
其中s 为顺序栈,x为入栈的元素,G 为有向图,采用邻接表存储。
裁判测试程序样例:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -1 #define MAXSIZE 30 #define VERTEX_MAX 30 #define VEX_NUM 10 typedef int Status;typedef char Vextype[20 ]; typedef int ElemType;typedef struct { ElemType elem[MAXSIZE]; int top; }SeqStack; typedef struct node { int adjvex; struct node *next; }EdgeNode; typedef struct vnode { int Indegree; Vextype vertex; EdgeNode *firstedge; }VertexNode; typedef struct { VertexNode adjlist[VERTEX_MAX]; int n,e; } ALGraph; void InitStack_Sq (SeqStack &s) { s.top=-1 ; } int Empty_Sq (SeqStack s) {if (s.top==-1 ) return 1 ; else return 0 ; } Status Push_SeqStack (SeqStack &s, ElemType x) { } Status Pop_SeqStack (SeqStack &s, ElemType &y) { if (Empty_Sq (s)) return OVERFLOW; else { y=s.elem[s.top]; s.top--; return OK; } } void CreateALGraph (ALGraph &G) { int i,v,w; int Indegree[VERTEX_MAX]={0 }; EdgeNode *s; scanf ("%d,%d" ,&(G.n),&(G.e)); for (i=0 ;i<G.n;i++) { scanf ("%s" ,G.adjlist[i].vertex); G.adjlist[i].firstedge=NULL ; } for (w=0 ;w<G.e;w++) { scanf ("%d,%d" ,&i,&v); s=(EdgeNode*)malloc (sizeof (EdgeNode)); s->adjvex=v; Indegree[v]++; s->next=G.adjlist[i].firstedge; G.adjlist[i].firstedge=s; } for (i=0 ;i<G.n;i++) G.adjlist[i].Indegree=Indegree[i]; } void topsort ( ALGraph &G) {} int main () { ALGraph g; CreateALGraph (g); printf ("拓扑序列为:" ); topsort (g); return 0 ; }
输入样例:
在这里给出一组输入。例如:
6,5 C1 C2 C3 C4 C5 C6 0,2 1,2 1,5 2,3 3,4
输出样例:
在这里给出相应的输出。例如:
解析
Status Push_SeqStack (SeqStack &s, ElemType x) { if (s.top == MAXSIZE - 1 ) { return OVERFLOW; } s.elem[++s.top] = x; return OK; } void topsort (ALGraph &G) { int i, v, w; int cnt = 0 ; EdgeNode *ptr; SeqStack st; InitStack_Sq (st); for (i = 0 ; i < G.n; i++) { if (G.adjlist[i].Indegree == 0 ) Push_SeqStack (st, i); } while (!Empty_Sq (st)) { Pop_SeqStack (st, v); printf ("%s " , G.adjlist[v].vertex); cnt++; ptr = G.adjlist[v].firstedge; while (ptr != NULL ) { w = ptr->adjvex; G.adjlist[w].Indegree--; if (G.adjlist[w].Indegree == 0 ) Push_SeqStack (st, w); ptr = ptr->next; } } if (cnt < G.n) printf ("后续无法输出!\n" ); }
2-1 列出连通集
分数 7
作者 陈越
单位 浙江大学
给定一个有 n 个顶点和 m 条边的无向图,请用深度优先遍历(DFS)和广度优先遍历(BFS)分别列出其所有的连通集。假设顶点从 0 到 n −1 编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。
输入格式:
输入第 1 行给出 2 个整数 n (0<n ≤10) 和 m ,分别是图的顶点数和边数。随后 m 行,每行给出一条边的两个端点。每行中的数字之间用 1 空格分隔。
输出格式:
按照"{ v 1 v 2 … v**k }"的格式,每行输出一个连通集。先输出 DFS 的结果,再输出 BFS 的结果。
输入样例:
输出样例:
{ 0 1 4 2 7 } { 3 5 } { 6 } { 0 1 2 7 4 } { 3 5 } { 6 }
解析
#include <iostream> #include <vector> #include <algorithm> using namespace std;int n, m; int arcs[10 ][10 ] = {0 }; int visited[10 ] = {0 }; void DFS (int v, vector<int >& conn_set) { visited[v] = 1 ; conn_set.push_back (v); for (int i = 0 ; i < n; ++i) { if (arcs[v][i] == 1 && !visited[i]) { DFS (i, conn_set); } } } void BFS (int v, vector<int >& conn_set) { int queue[10 ]; int front = 0 , rear = 0 ; visited[v] = 1 ; queue[rear++] = v; conn_set.push_back (v); while (front < rear) { int u = queue[front++]; for (int i = 0 ; i < n; ++i) { if (arcs[u][i] == 1 && !visited[i]) { visited[i] = 1 ; conn_set.push_back (i); queue[rear++] = i; } } } } void print_conn_set (const vector<int >& conn_set) { cout << "{ " ; for (int i = 0 ; i < conn_set.size (); ++i) { if (i > 0 ) cout << " " ; cout << conn_set[i]; } cout << " }" << endl; } int main () { cin >> n >> m; for (int k = 0 ; k < m; ++k) { int u, v; cin >> u >> v; arcs[u][v] = 1 ; arcs[v][u] = 1 ; } for (int i = 0 ; i < n; ++i) { if (!visited[i]) { vector<int > conn_set; DFS (i, conn_set); print_conn_set (conn_set); } } fill (visited, visited + 10 , 0 ); for (int i = 0 ; i < n; ++i) { if (!visited[i]) { vector<int > conn_set; BFS (i, conn_set); print_conn_set (conn_set); } } return 0 ; }
2-2 旅游规划
分数 7
作者 陈越
单位 浙江大学
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第 1 行给出 4 个正整数 n 、m 、s 、d ,其中 n (2≤n ≤500)是城市的个数,顺便假设城市的编号为 0~(n −1);m 是高速公路的条数;s 是出发地的城市编号;d 是目的地的城市编号。随后的 m 行中,每行给出一条高速公路的信息,分别是:城市 1、城市 2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过 500。输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3 0 1 1 20 1 3 2 30 0 3 4 10 0 2 2 20 2 3 1 20
输出样例:
解析
#include <stdio.h> #include <string.h> #define INF 1000000000 #define MAXN 501 int main () { int n, m, s, d; scanf ("%d %d %d %d" , &n, &m, &s, &d); int dist[MAXN][MAXN], cost[MAXN][MAXN]; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < n; j++) { dist[i][j] = INF; cost[i][j] = INF; } dist[i][i] = 0 ; cost[i][i] = 0 ; } for (int i = 0 ; i < m; i++) { int a, b, len, fee; scanf ("%d %d %d %d" , &a, &b, &len, &fee); dist[a][b] = len; dist[b][a] = len; cost[a][b] = fee; cost[b][a] = fee; } int min_dist[MAXN], min_cost[MAXN]; bool visited[MAXN]; memset (visited, false , sizeof (visited)); for (int i = 0 ; i < n; i++) { min_dist[i] = dist[s][i]; min_cost[i] = cost[s][i]; } visited[s] = true ; for (int i = 0 ; i < n - 1 ; i++) { int u = -1 ; int min_len = INF; for (int j = 0 ; j < n; j++) { if (!visited[j] && min_dist[j] < min_len) { min_len = min_dist[j]; u = j; } } if (u == -1 ) break ; visited[u] = true ; for (int v = 0 ; v < n; v++) { if (!visited[v] && dist[u][v] != INF) { if (min_dist[v] > min_dist[u] + dist[u][v]) { min_dist[v] = min_dist[u] + dist[u][v]; min_cost[v] = min_cost[u] + cost[u][v]; } else if (min_dist[v] == min_dist[u] + dist[u][v]) { if (min_cost[v] > min_cost[u] + cost[u][v]) { min_cost[v] = min_cost[u] + cost[u][v]; } } } } } printf ("%d %d\n" , min_dist[d], min_cost[d]); return 0 ; }
2-3 公路村村通
分数 7
作者 陈越
单位 浙江大学
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数 n (≤1000)和候选道路数目 m (≤3n );随后的 m 行对应 m 条道路,每行给出 3 个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从 1 到 n 编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出 −1,表示需要建设更多公路。
输入样例:
6 15 1 2 5 1 3 3 1 4 7 1 5 4 1 6 2 2 3 4 2 4 6 2 5 2 2 6 6 3 4 6 3 5 1 3 6 1 4 5 10 4 6 8 5 6 3
输出样例:
解析
#include <iostream> #include <vector> #include <algorithm> using namespace std;struct Edge { int u; int v; int weight; bool operator <(const Edge& other) const { return weight < other.weight; } }; const int MAXN = 1001 ; int parent[MAXN]; int rank_[MAXN]; void initUnionFind (int n) { for (int i = 1 ; i <= n; ++i) { parent[i] = i; rank_[i] = 1 ; } } int findRoot (int x) { if (parent[x] != x) { parent[x] = findRoot (parent[x]); } return parent[x]; } bool unionSets (int x, int y) { int rootX = findRoot (x); int rootY = findRoot (y); if (rootX == rootY) { return false ; } if (rank_[rootX] < rank_[rootY]) { parent[rootX] = rootY; } else { parent[rootY] = rootX; if (rank_[rootX] == rank_[rootY]) { rank_[rootX]++; } } return true ; } int main () { int n, m; cin >> n >> m; vector<Edge> edges (m) ; for (int i = 0 ; i < m; ++i) { cin >> edges[i].u >> edges[i].v >> edges[i].weight; } if (n == 1 ) { cout << 0 << endl; return 0 ; } sort (edges.begin (), edges.end ()); initUnionFind (n); int totalCost = 0 ; int validEdges = 0 ; for (const auto & edge : edges) { if (unionSets (edge.u, edge.v)) { totalCost += edge.weight; validEdges++; if (validEdges == n - 1 ) { break ; } } } if (validEdges == n - 1 ) { cout << totalCost << endl; } else { cout << -1 << endl; } return 0 ; }
2-4 最短工期
分数 7
作者 陈越
单位 浙江大学
一个项目由若干个任务组成,任务之间有先后依赖顺序。项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务。现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工时间。
输入格式:
首先第一行给出两个正整数:项目里程碑的数量 N (≤100)和任务总数 M 。这里的里程碑从 0 到 N −1 编号。随后 M 行,每行给出一项任务的描述,格式为“任务起始里程碑 任务结束里程碑 工作时长”,三个数字均为非负整数,以空格分隔。
输出格式:
如果整个项目的安排是合理可行的,在一行中输出最早完工时间;否则输出"Impossible"。
输入样例 1:
9 12 0 1 6 0 2 4 0 3 5 1 4 1 2 4 1 3 5 2 5 4 0 4 6 9 4 7 7 5 7 4 6 8 2 7 8 4
输出样例 1:
输入样例 2:
4 5 0 1 1 0 2 2 2 1 3 1 3 4 3 2 5
输出样例 2:
解析
#include <stdio.h> #include <stdlib.h> #define MAXN 101 typedef struct Edge { int v; int w; struct Edge *next; } Edge; Edge *adj[MAXN]; int in_degree[MAXN]; int earliest[MAXN]; int N, M; void init () { for (int i = 0 ; i < MAXN; i++) { adj[i] = NULL ; in_degree[i] = 0 ; earliest[i] = 0 ; } } void addEdge (int u, int v, int w) { Edge *e = (Edge *)malloc (sizeof (Edge)); e->v = v; e->w = w; e->next = adj[u]; adj[u] = e; } int main () { init (); scanf ("%d %d" , &N, &M); for (int i = 0 ; i < M; i++) { int u, v, w; scanf ("%d %d %d" , &u, &v, &w); addEdge (u, v, w); in_degree[v]++; } int q[MAXN], front = 0 , rear = 0 ; for (int i = 0 ; i < N; i++) { if (in_degree[i] == 0 ) { q[rear++] = i; } } int count = 0 ; while (front < rear) { int u = q[front++]; count++; Edge *p = adj[u]; while (p != NULL ) { int v = p->v; int w = p->w; if (earliest[v] < earliest[u] + w) { earliest[v] = earliest[u] + w; } in_degree[v]--; if (in_degree[v] == 0 ) { q[rear++] = v; } p = p->next; } } if (count != N) { printf ("Impossible\n" ); return 0 ; } int max_earliest = 0 ; for (int i = 0 ; i < N; i++) { if (earliest[i] > max_earliest) { max_earliest = earliest[i]; } } printf ("%d\n" , max_earliest); for (int i = 0 ; i < N; i++) { Edge *p = adj[i]; while (p != NULL ) { Edge *temp = p; p = p->next; free (temp); } } return 0 ; }
2-5 图的连通性判断
分数 7
作者 陈越
单位 浙江大学
请编写程序,用广度优先搜索输出给定无向图中的各个连通分量,并判断给定的无向图是否连通。
注意输出顺序规定如下:
每个连通分量的输出从其中编号最小的顶点开始;
不同连通分量按其第一个顶点的编号增序输出,每个连通分量占一行;
当一个顶点有多个邻接点时,按其输入的逆序进行访问,即最后输入的邻接点,在广度优先搜索中应被最先访问输出。
输入格式:
输入首先在第一行给出图中最大顶点数量,即正整数 kMaxVertex(≤10)。
第二行给出两个正整数,依次为当前要创建的图的顶点数 n 和边数 m (保证顶点数至少为 2 且不超过最大顶点数量)。
第三行给出 n 个英文字母,顺序对应每个顶点的信息。
随后 m 行,每行给出一条无向 边的两个端点的编号、以及边的权重。顶点编号从 0 开始,权重(≤230)为整数。
同行数字、字符均以一个空格分隔。
输出格式:
按照题面中规定的顺序,输出图中的连通分量每个顶点的信息。每个连通分量占一行,字符间不要有空格。
随后,如果图不连通 ,则在一行中输出 有 x 个连通分量,其中 x 是连通分量的个数。
最后一行输出 该图连通性为 y,其中图连通时 y 值为 1,否则为 0。
输入样例:
10 6 7 a b c d e f 2 0 2 2 3 1 3 0 3 3 4 4 2 4 2 4 0 5 4 5 1
输出样例:
解析
#include <bits/stdc++.h> using namespace std;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int kMaxVertex; if (!(cin >> kMaxVertex)) return 0 ; int n, m; cin >> n >> m; vector<char > info (n) ; for (int i = 0 ; i < n; i++) cin >> info[i]; vector<vector<int >> adj (n); for (int i = 0 ; i < m; i++) { int u, v; long long w; cin >> u >> v >> w; adj[u].push_back (v); adj[v].push_back (u); } vector<int > visited (n, 0 ) ; vector<vector<int >> components; for (int start = 0 ; start < n; start++) { if (!visited[start]) { queue<int > q; vector<int > comp; visited[start] = 1 ; q.push (start); while (!q.empty ()) { int u = q.front (); q.pop (); comp.push_back (u); for (int i = (int )adj[u].size () - 1 ; i >= 0 ; --i) { int v = adj[u][i]; if (!visited[v]) { visited[v] = 1 ; q.push (v); } } } components.push_back (comp); } } for (auto &comp : components) { for (int v : comp) cout << info[v]; cout << "\n" ; } int cnt = (int )components.size (); if (cnt > 1 ) { cout << "有 " << cnt << " 个连通分量\n" ; cout << "该图连通性为 0\n" ; } else { cout << "该图连通性为 1\n" ; } return 0 ; }
2-6 求最小生成树的Prim算法
分数 8
作者 陈越
单位 浙江大学
请编写程序,实现在带权的无向图中求最小生成树的 Prim 算法。
注意:当多个待收录顶点到当前点集的距离等长时,按编号升序进行收录。
输入格式:
输入首先在第一行给出两个正整数,依次为当前要创建的图的顶点数 n (≤100)和边数 m 。
随后 m 行,每行给出一条无向边两端点的编号、权重。顶点编号从 0 开始,权重(≤100)为整数。同行数字均以一个空格分隔。
输出格式:
参考样例。
首先在一行中输出 total weight = x,其中 x 为最小生成树中所有边的总权重。如果最小生成树不存在,则 x 为 −1。
随后在一行中按顶点编号升序输出每个顶点在最小生成树中的父结点的编号。为输出简单起见,每个数字后有一个空格。
注意:此处默认顶点 0 为最小生成树的根结点,其父结点编号规定为 −1。算法初始化时,所有顶点(除了根结点)的父结点编号默认为 0。
输入样例 1:
6 9 0 1 1 0 2 3 1 2 6 1 3 2 2 3 7 2 4 5 2 5 4 3 5 8 4 5 1
输出样例 1:
total weight = 11 -1 0 0 1 5 2
输入样例 2:
7 9 0 1 1 0 2 3 1 2 6 1 3 2 2 3 7 2 4 5 2 5 4 3 5 8 4 5 1
输出样例 2:
total weight = -1 -1 0 0 1 5 2 0
解析
#include <stdio.h> #include <stdbool.h> #define INF 1000 #define MAXN 101 int main () { int n, m; scanf ("%d %d" , &n, &m); int adj[MAXN][MAXN]; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < n; j++) { adj[i][j] = INF; } adj[i][i] = 0 ; } for (int i = 0 ; i < m; i++) { int u, v, w; scanf ("%d %d %d" , &u, &v, &w); adj[u][v] = w; adj[v][u] = w; } int dist[MAXN], parent[MAXN]; bool visited[MAXN]; int total_weight = 0 ; int count = 0 ; for (int i = 0 ; i < n; i++) { dist[i] = adj[0 ][i]; parent[i] = 0 ; visited[i] = false ; } parent[0 ] = -1 ; visited[0 ] = true ; for (int i = 0 ; i < n - 1 ; i++) { int min_dist = INF; int u = -1 ; for (int j = 0 ; j < n; j++) { if (!visited[j] && dist[j] < min_dist) { min_dist = dist[j]; u = j; } } if (u == -1 ) { total_weight = -1 ; break ; } visited[u] = true ; total_weight += min_dist; count++; for (int v = 0 ; v < n; v++) { if (!visited[v] && adj[u][v] < dist[v]) { dist[v] = adj[u][v]; parent[v] = u; } } } if (count != n - 1 ) { total_weight = -1 ; } printf ("total weight = %d\n" , total_weight); for (int i = 0 ; i < n; i++) { printf ("%d " , parent[i]); } printf ("\n" ); return 0 ; }
2-7 拓扑排序
分数 8
作者 陈越
单位 浙江大学
请编写程序,实现对有向无权图中的顶点进行拓扑排序的算法。
注意:如果拓扑序不唯一,输出任何一个序列都可以,由特殊裁判程序判定正确性。
输入格式:
输入首先在第一行给出两个正整数,依次为当前要创建的图的顶点数 n (≤100)和边数 m 。
随后一行顺序给出 n 个顶点对应的字符串,由不超过 3 个英文字母或数字组成。
接下来 m 行,每行给出一条有向边的起点编号、终点编号。顶点编号从 0 开始。同行数字和字符串均以一个空格分隔。
输出格式:
参考样例。
首先在一行中输出 该图拓扑序存在性为 x,其中 x 为 1 表示该图顶点有拓扑序,为 0 表示没有。
随后在一行中按顶点拓扑序存输出每个顶点对应的字符串。为输出简单起见,每个字符串后有一个空格。
注意:如果拓扑序不存在,最后一行可以输出任何字符,均判为正确。
输入样例 1:
8 8 C0 C1 C2 C3 C4 C5 C6 C7 0 2 0 4 1 2 1 4 3 4 4 5 4 6 5 7
输出样例 1:
该图拓扑序存在性为 1 C0 C1 C3 C2 C4 C6 C5 C7
输入样例 2:
8 9 C0 C1 C2 C3 C4 C5 C6 C7 0 2 0 4 1 2 1 4 3 4 4 5 4 6 5 7 7 4
输出样例 2:
解析
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 101 typedef struct EdgeNode { int adjvex; struct EdgeNode *next; } EdgeNode; typedef struct VNode { char data[4 ]; EdgeNode *firstedge; } VNode; typedef struct { VNode adjlist[MAXN]; int in_degree[MAXN]; int n, m; } ALGraph; int main () { ALGraph G; int i; scanf ("%d %d" , &G.n, &G.m); for (i = 0 ; i < G.n; i++) { scanf ("%s" , G.adjlist[i].data); G.adjlist[i].firstedge = NULL ; G.in_degree[i] = 0 ; } for (i = 0 ; i < G.m; i++) { int u, v; scanf ("%d %d" , &u, &v); EdgeNode *p = (EdgeNode *)malloc (sizeof (EdgeNode)); p->adjvex = v; p->next = G.adjlist[u].firstedge; G.adjlist[u].firstedge = p; G.in_degree[v]++; } int q[MAXN], front = 0 , rear = 0 ; int topo_seq[MAXN], count = 0 ; for (i = 0 ; i < G.n; i++) { if (G.in_degree[i] == 0 ) { q[rear++] = i; } } while (front < rear) { int u = q[front++]; topo_seq[count++] = u; EdgeNode *p = G.adjlist[u].firstedge; while (p != NULL ) { int v = p->adjvex; G.in_degree[v]--; if (G.in_degree[v] == 0 ) { q[rear++] = v; } p = p->next; } } int exist = (count == G.n) ? 1 : 0 ; printf ("该图拓扑序存在性为 %d\n" , exist); if (exist) { for (i = 0 ; i < G.n; i++) { printf ("%s " , G.adjlist[topo_seq[i]].data); } } printf ("\n" ); for (i = 0 ; i < G.n; i++) { EdgeNode *p = G.adjlist[i].firstedge; while (p != NULL ) { EdgeNode *temp = p; p = p->next; free (temp); } } return 0 ; }
2-8 求图中关键活动
分数 8
作者 陈越
单位 浙江大学
请编写程序,实现求带权的有向图中关键活动的算法。
输入格式:
输入首先在第一行给出两个正整数,依次为当前要创建的图的顶点数 n (≤100)和边数 m 。
随后 m 行,每行给出一条有向 边的起点编号、终点编号、权重。顶点编号从 0 开始,权重(≤100)为整数。同行数字均以一个空格分隔。
输出格式:
按格式 <u, v> 输出关键活动,其中 u 为起点编号,v 为终点编号。按起点编号的升序,每行输出一个活动。起点编号相同时,与输入的顺序相反,即先输入的边后输出。
最后一行输出 关键路径分析结果为 x,其中 x 为 1 表示关键路径已成功求出,为 0 表示不成功。
输入样例 1:
9 12 0 1 5 0 2 3 1 4 4 1 5 2 2 3 6 2 4 1 3 6 7 4 6 3 4 7 5 5 7 6 6 8 2 7 8 8
输出样例 1:
<0, 1> <1, 4> <4, 7> <7, 8> 关键路径分析结果为 1
输入样例 2:
8 9 0 2 3 0 4 5 1 2 12 1 4 7 3 4 9 4 5 2 4 6 1 5 7 2 7 4 41
输出样例 2:
解析
#include <bits/stdc++.h> using namespace std;struct Edge { int v, w, id; }; int main () { int n, m; cin >> n >> m; vector<vector<Edge>> G (n); vector<vector<Edge>> rG (n); vector<int > indeg (n, 0 ) ; vector<tuple<int ,int ,int ,int >> edges; for (int i = 0 ; i < m; i++) { int u, v, w; cin >> u >> v >> w; G[u].push_back ({v, w, i}); rG[v].push_back ({u, w, i}); indeg[v]++; edges.push_back ({u, v, w, i}); } queue<int > q; vector<int > topo; for (int i = 0 ; i < n; i++) if (indeg[i] == 0 ) q.push (i); while (!q.empty ()) { int u = q.front (); q.pop (); topo.push_back (u); for (auto &e : G[u]) { indeg[e.v]--; if (indeg[e.v] == 0 ) q.push (e.v); } } if ((int )topo.size () < n) { cout << "关键路径分析结果为 0" ; return 0 ; } vector<int > ve (n, 0 ) ; for (int u : topo) { for (auto &e : G[u]) { ve[e.v] = max (ve[e.v], ve[u] + e.w); } } vector<int > vl (n, ve[topo.back()]) ; for (int i = topo.size () - 1 ; i >= 0 ; i--) { int v = topo[i]; for (auto &e : G[v]) { vl[v] = min (vl[v], vl[e.v] - e.w); } } if (vl[0 ] < 0 ) { cout << "关键路径分析结果为 0" ; return 0 ; } vector<pair<int , pair<int , int >>> critical; for (auto &[u, v, w, id] : edges) { int e = ve[u]; int l = vl[v] - w; if (e == l) { critical.push_back ({u, {v, id}}); } } sort (critical.begin (), critical.end (), [](auto &a, auto &b) { if (a.first != b.first) return a.first < b.first; return a.second.second > b.second.second; }); for (auto &p : critical) { cout << "<" << p.first << ", " << p.second.first << ">\n" ; } cout << "关键路径分析结果为 1" ; return 0 ; }
2-9 团结就是力量
分数 9
作者 陈越
单位 浙江大学
常言道:团结就是力量。这里我们定义一个群体是“团结”的,如果这个群体中的任意两个人都是好朋友。并且,我们假定友谊是双向且可传递的,即:若 A 和 B 是朋友、B 和 C 是朋友,则 A 和 C 一定是朋友。下面给定一些人与人之间的关系,你的任务是用最少的努力将他们全部团结起来。
输入格式:
首先在第一行给出两个正整数 n 和 m (均不超过 105),分别为总人数和人与人之间的关系条数。接下来 m 行,每行描述了两个人之间的关系,格式为:
其中 P1 和 P2 是两个人的编号(所有人从 1 到 n 编号),力量值 是区间 [−103,103] 内的一个非零整数,其为正数时,表示 P1 和 P2 是朋友,这个值是他们团结起来的力量;其为负数时,表示 P1 和 P2 还没有成为朋友,这个值的绝对值是团结他们所需要付出的力量。
此外,如果我们想要将 A 和 B 团结起来,但他们之间的关系并没有给出,则假设团结他们需要付出的力量是个常数 104。
输出格式:
根据输入的人际关系,所有人可以被划分为几个朋友群。一个朋友群的“凝聚力”被定义为这个群中所有朋友间最小的力量值。首先必须在一行中按以下格式输出这些朋友群的信息:
其中 Gi 是朋友群的编号,即该群成员的最小编号;Si 是该群的凝聚力。按凝聚力的非递增序输出。当凝聚力并列时,按群规模(即群成员人数)的非递增序输出。如果还是并列,则按群编号的增序输出。一对数据之间用1个空格分隔,行首尾不得有多余空格。
随后在下一行输出将所有人全部团结起来需要的最小力量值。
输入样例:
10 13 8 3 -5 8 6 3 2 5 2 1 8 -1 3 5 -7 7 9 4 9 4 5 7 3 -3 4 3 -4 6 1 2 8 2 -6 7 4 2 6 3 -2
输出样例:
1-2 4-2 2-2 3-0 10-0 10011
解析
#include <iostream> #include <vector> #include <algorithm> #include <unordered_map> #include <unordered_set> #include <climits> using namespace std;const int MAXN = 1e5 + 10 ;const int INF = INT_MAX;const int DEFAULT = 10000 ;int parent1[MAXN];int size1[MAXN];int min_pos1[MAXN]; int min_node1[MAXN]; bool visited[MAXN]; int gi_of_root[MAXN];int find2 (int x, unordered_map<int , int >& parent2_map) { if (parent2_map[x] != x) { parent2_map[x] = find2 (parent2_map[x], parent2_map); } return parent2_map[x]; } int find1 (int x) { if (parent1[x] != x) { parent1[x] = find1 (parent1[x]); } return parent1[x]; } struct Group { int Gi; int Si; int size; bool operator <(const Group& other) const { if (Si != other.Si) return Si > other.Si; if (size != other.size) return size > other.size; return Gi < other.Gi; } }; struct NegEdge { int u, v, w; }; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int n, m; cin >> n >> m; for (int i = 1 ; i <= n; ++i) { parent1[i] = i; size1[i] = 1 ; min_pos1[i] = INF; min_node1[i] = i; visited[i] = false ; } vector<NegEdge> neg_edges; for (int i = 0 ; i < m; ++i) { int u, v, w; cin >> u >> v >> w; if (w > 0 ) { int root_u = find1 (u); int root_v = find1 (v); if (root_u != root_v) { if (size1[root_u] < size1[root_v]) swap (root_u, root_v); parent1[root_v] = root_u; size1[root_u] += size1[root_v]; min_pos1[root_u] = min ({min_pos1[root_u], min_pos1[root_v], w}); min_node1[root_u] = min (min_node1[root_u], min_node1[root_v]); } else { if (w < min_pos1[root_u]) min_pos1[root_u] = w; } } else { neg_edges.push_back ({u, v, w}); } } vector<Group> groups; for (int i = 1 ; i <= n; ++i) { int root = find1 (i); if (!visited[root]) { visited[root] = true ; Group g; g.Gi = min_node1[root]; g.size = size1[root]; g.Si = (g.size == 1 ) ? 0 : min_pos1[root]; groups.push_back (g); gi_of_root[root] = g.Gi; } } sort (groups.begin (), groups.end ()); int k = groups.size (); for (int i = 0 ; i < k; ++i) { if (i > 0 ) cout << " " ; cout << groups[i].Gi << "-" << groups[i].Si; } cout << "\n" ; if (k == 1 ) { cout << 0 << "\n" ; return 0 ; } unordered_map<int , int > parent2_map; for (auto & g : groups) parent2_map[g.Gi] = g.Gi; unordered_map<int , unordered_map<int , int >> edge_map; for (auto & e : neg_edges) { int root_u = find1 (e.u); int root_v = find1 (e.v); int g1 = gi_of_root[root_u]; int g2 = gi_of_root[root_v]; if (g1 == g2) continue ; int a = min (g1, g2), b = max (g1, g2); int abs_w = -e.w; if (edge_map[a].count (b) == 0 || abs_w < edge_map[a][b]) { edge_map[a][b] = abs_w; } } vector<pair<int , pair<int , int >>> mst_edges; for (auto & p1 : edge_map) { int a = p1.f irst; for (auto & p2 : p1. second) { int b = p2.f irst, w = p2. second; mst_edges.emplace_back (w, make_pair (a, b)); } } sort (mst_edges.begin (), mst_edges.end ()); long long total_effort = 0 ; for (auto & e : mst_edges) { int w = e.first; int a = e.second.first, b = e.second.second; int root_a = find2 (a, parent2_map); int root_b = find2 (b, parent2_map); if (root_a != root_b) { parent2_map[root_b] = root_a; total_effort += w; } } unordered_set<int > root_set; for (auto & g : groups) root_set.insert (find2 (g.Gi, parent2_map)); int c = root_set.size (); total_effort += (long long )(c - 1 ) * DEFAULT; cout << total_effort << "\n" ; return 0 ; }