第2关:基于Dijsktra算法的最短路径求解
任务描述
一张地图包括n个城市,假设城市间有m条路径(有向图),每条路径的长度已知。给定地图的一个起点城市和终点城市,利用Dijsktra算法求出起点到终点之间的最短路径。
编程要求
输入
多组数据,每组数据有m+3行。第一行为两个整数n和m,分别代表城市个数n和路径条数m。第二行有n个字符,代表每个城市的名字。第三行到第m+2行每行有两个字符a和b和一个整数d,代表从城市a到城市b有一条距离为d的路。最后一行为两个字符,代表待求最短路径的城市起点和终点。当n和m都等于0时,输入结束。
输出
每组数据输出两行。第一行为一个整数,为从起点到终点之间最短路的长度。第二行为一串字符串,代表该路径。每两个字符之间用空格隔开。
测试说明
平台会对你编写的代码进行测试:
测试输入:
3 3
A B C
A B 1
B C 1
A C 3
A C
6 8
A B C D E F
A F 100
A E 30
A C 10
B C 5
C D 50
E D 20
E F 60
D F 10
A F
0 0
预期输出:
2
A B C
60
A E D F
#include<iostream>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MaxInt 32767 //最大值
#define MVNum 100 //最大数量
using namespace std;
int D[MVNum]; //D[i]记录从源到终端的当前最短
int Path[MVNum]; //路径[i]记录是从源到终端上vi的当前最小序列号
typedef struct
{
char vexs[MVNum]; //点列表
int arcs[MVNum][MVNum]; //连接到短端口阵列
int vexnum; //数字总数
int arcnum; //图中GL的总数
} AMGraph;
int LocateVex(AMGraph G,char u)
{//存储在“返回”表中的标签,无论是否返回
int i;
for(i=0;i<G.vexnum;i++)
if(u==G.vexs[i])
return i;
return ERROR;
}
char OrigialVex(AMGraph G,int u)
{//以下文件u的元素作为表存储在返回表中?
return G.vexs[u];
}
int CreateDN(AMGraph &G,int spot,int edge)
{//使用连接LPG阵列的方法创建定向屏蔽
G.vexnum=spot;
G.arcnum=edge;
int i,j,k,w;
char v1,v2;
for(i=0;i<G.vexnum;++i)
cin>>G.vexs[i]; //输入
for(i=0;i<G.vexnum;++i)
for(j=0;j<G.vexnum;++j)
G.arcs[i][j]=MaxInt;
for(k=0;k<G.arcnum;++k)
{
cin>>v1>>v2>>w; ///输入附着到第一个面板的标签
i=LocateVex(G, v1);
j=LocateVex(G, v2); //确定v1和2之间的位置
G.arcs[i][j]=w;
}//for
return OK;
}
void ShortestPath_DIJ(AMGraph G, char V)
{//用Dijkstra算法求有向网G的v0顶点到其余顶点的最短路径
/**************begin************/
int v, w, i, min;
bool final[MVNum]; //final[w] = true表示已经找到从v到w的最短路径
v = LocateVex(G, V);
for (i = 0; i < G.vexnum; ++i)
{
final[i] = false; //所有的点开始时都是未知最短路径的
D[i] = G.arcs[v][i]; //将与v点有连线的点加上权值
if (D[i] < MaxInt) //如果i点与v点本身连通,那么将从v点到i点的最短路径设置成i点
Path[i] = v;
else
Path[i] = -1;
}
D[v] = 0;
final[v] = true; //对源点进行初始化
for (i = 1; i < G.vexnum; ++i) //其余G.vexnum-1个顶点
{
min = MaxInt; //当前所知离v顶点的最近距离
for (w = 0; w < G.vexnum; ++w)
if (!final[w] && D[w] < min)
{
v = w;
min = D[w]; //w顶点离v顶点更近
}
final[v] = true; //离v顶点最近的v加入S集
for (w = 0; w < G.vexnum; ++w) //更新当前最短路径及距离
if (!final[w] && (min + G.arcs[v][w] < D[w]))
{
D[w] = min + G.arcs[v][w];
Path[w] = v;
}
}
/**************end************/
}
void Find(AMGraph G,int v)
{//在路径编号组中查找程序编号
if(Path[v]==-1)
return ;
else
{
Find(G,Path[v]);
cout<<OrigialVex(G,Path[v])<<" "; //输出点列表中标记有路径[v]的元素
}
}
int main()
{
char a,b;
int n,m;
while(cin>>n>>m)
{
if(n==0&&m==0) break;
AMGraph G;
CreateDN(G,n,m); //创建定向屏蔽
cin>>a; //输入
ShortestPath_DIJ(G,a); //如果G的接触点和它的静止点之间有一段短时间
cin>>b; //触点底座b
int v=LocateVex(G,b);//返回地面表中的下一个标记,以验证测量值的数量
cout<<D[v]<<endl; //将它们之间的短距离输出到B
Find(G,v);
cout<<b<<endl;
}
return 0;
}
第3关:六度空间理论
任务描述
“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”假如给你一个社交网络图,请你对每个节点计算符合“六度空间”理论的结点占结点总数的百分比。
编程要求
输入
多组数据,每组数据m+1行。第一行有两个数字n和m,代表有n个人和m组朋友关系。n个人的编号为1到n。第二行到第m+1行每行包括两个数字a和b,代表这两个人互相认识。当n和m都等于0时,输入结束。
输出
每组数据输出n行,对每个结点输出与该结点距离不超过6的结点数占结点总数的百分比,精确到小数点后2位。每个结节点输出一行,格式为“结点编号:(空格)百分比%”。
测试说明
平台会对你编写的代码进行测试:
测试输入:
10 9
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 8
1 2
2 3
3 4
4 5
5 6
6 7
7 8
9 10
0 0
预期输出:
1: 70.00%
2: 80.00%
3: 90.00%
4: 100.00%
5: 100.00%
6: 100.00%
7: 100.00%
8: 90.00%
9: 80.00%
10: 70.00%
1: 70.00%
2: 80.00%
3: 80.00%
4: 80.00%
5: 80.00%
6: 80.00%
7: 80.00%
8: 70.00%
9: 20.00%
10: 20.00%
#include <iostream>
#include <iomanip>
#define MAXSIZE 100
#define INFINITE 32767
using namespace std;
void CreateUDG(int G[][MAXSIZE], int m, int n)
{//创建无向图的邻接矩阵,n个人,m组朋友关系
for (int i=0;i<n;i++) //初始化为无穷大
for (int j=0;j<n;j++)
G[i][j]=INFINITE;
int a, b;
for (int i=0;i<m;i++) { //有边则改为1
cin>>a>>b;
G[a-1][b-1]=1;
G[b-1][a-1]=1;
}
}
int FindMinPath(bool S[], int D[], int n)
{//根据当前的D,选择最小的那个D[i],即为到起点的最短距离,对应的顶点下标为i
int min=INFINITE;
int index=-1;
for (int i=0;i<n;i++) {
if (!S[i]&&D[i]<min) {
min=D[i];
index=i;
}
}
return index;
}
float SixDegree_DIJ(int G[][MAXSIZE], int n, int start)
{//求从start到其他各顶点路径小于7的个数
/**************begin************/
bool S[MAXSIZE]={false};
int D[MAXSIZE];
for (int i=0;i<n;i++) {
D[i]=G[start][i];
}
S[start]=true;
for (int i=1;i<n;i++) {
int v=FindMinPath(S,D,n);
if (v == -1) break;
S[v]=true;
for (int w=0;w<n;w++) {
if (!S[w] && G[v][w] != INFINITE && D[v]+G[v][w]<D[w]) {
D[w]=D[v]+G[v][w];
}
}
}
int count = 0; // 计数小于等于6的结点数
for (int i = 0; i < n; i++) {
if (i != start && D[i] <= 6) {
count++;
}
}
return float(count+1) / n ;
/**************end************/
}
int main()
{
int n,m;
while (true) {
cin>>n>>m;
if (n==0&&m==0) break;
int G[MAXSIZE][MAXSIZE]={ 0 };
CreateUDG(G,m,n);
for (int i=0;i<n;i++) { //每个顶点作为start,输出路径长度小于等于6的个数的比例
cout<<i+1<<": "<<fixed<<setprecision(2)<<SixDegree_DIJ(G,n,i)*100<<"%"<<endl;
}
}
return 0;
}
第11关:基于邻接表的深度优先遍历
任务描述
一个连通图采用邻接表作为存储结构。设计一个算法,实现从顶点v出发的深度优先遍历的非递归过程。
编程要求
输入
多组数据,每组m+2数据行。第一行有两个数字n和m,代表有n个顶点和m条边。顶点编号为1到n。第二行到第m+1行每行有两个整数h和k,代表边依附的两个顶点。第m+2行有一个整数d,代表从d开始遍历。当n和m都等于0时,输入结束。
输出
每组数据输出一行,为深度优先搜索的遍历结果。每两个数字之间用空格隔开。
测试说明
平台会对你编写的代码进行测试:
测试输入:
3 2
1 2
1 3
1
2 1
1 2
2
0 0
预期输出:
1 2 3
2 1
#include<iostream>
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100
using namespace std;
typedef struct ArcNode
{//边结点
int data;
struct ArcNode *nextarc; //链域:指向下一条边的指针
}ArcNode;
typedef struct VNode
{//顶点信息
int data;
ArcNode *firstarc; //链域:指向第一条依附该顶点的边的指针
}VNode,AdjList[MAXSIZE]; //AdjList表示邻接表类型
typedef struct
{//邻接表
AdjList vertices;
int vexnum,arcnum; //图的当前顶点数和边数
}ALGraph;
typedef struct
{//顺序栈
int *base; //栈底指针
int *top; //栈顶指针
int stacksize; //栈可用的最大容量
}SqStack;
void InitStack(SqStack &S)
{//顺序栈的初始化
S.base=new int[MAXSIZE]; //动态分配一个最大容量MAXSIZE的数组空间
S.top=S.base; //top初始为base,空栈
S.stacksize=MAXSIZE;
}
void Push(SqStack &S,int e)
{//入栈操作
if(S.top-S.base==S.stacksize) //栈满
return;
*S.top=e; //元素e压入栈顶
S.top++; //栈顶指针加1
}
void Pop(SqStack &S,int &e)
{//出栈操作
if(S.base==S.top) //栈空
return;
S.top--; //栈顶指针减1
e=*S.top; //将栈顶元素赋给e
}
bool StackEmpty(SqStack S)
{//判空操作
if(S.base==S.top) //栈空返回true
return true;
return false;
}
bool visited[MAXSIZE]; //访问标志数组,初始为false
int CreateUDG(ALGraph &G,int vexnum,int arcnum)
{//采用邻接表表示法,创建无向图G
G.vexnum=vexnum; //输入总顶点数
G.arcnum=arcnum; //输入总边数
if(G.vexnum>MAXSIZE) return ERROR; //超出最大顶点数则结束函数
int i,h,k;
for(i=1;i<=G.vexnum;i++) //构造表头结点表
{
G.vertices[i].data=i;
visited[i]=false;
G.vertices[i].firstarc=NULL;
}
ArcNode *p1,*p2;
for(i=0;i<G.arcnum;i++) //输入各边,头插法构造邻接表
{
cin>>h>>k;
p1=new ArcNode;
p1->data=k;
p1->nextarc=G.vertices[h].firstarc;
G.vertices[h].firstarc=p1;
p2=new ArcNode;
p2->data=h;
p2->nextarc=G.vertices[k].firstarc;
G.vertices[k].firstarc=p2;
}
return OK;
}
void DFS(ALGraph G,int v,SqStack S)
{//从第v个顶点出发非递归实现深度优先遍历图G
/**************begin************/
InitStack(S);
Push(S, v);
visited[v] = true;
bool firstVertex = true;
while (!StackEmpty(S)) {
Pop(S, v);
if (!firstVertex) {
cout << " ";
} else {
firstVertex = false;
}
cout << v;
ArcNode* p = G.vertices[v].firstarc;
while (p) {
if (!visited[p->data]) {
Push(S, p->data);
visited[p->data] = true;
}
p = p->nextarc;
}
}
cout << endl;
/**************end************/
}
int main()
{
int n,m;
while(cin>>n>>m)
{
if(n==0&&m==0) break;
ALGraph G;
SqStack S;
CreateUDG(G,n,m); //创建无向图G
int d; //从d开始遍历
cin>>d;
DFS(G,d,S); //基于邻接表的深度优先遍历
}
return 0;
}