1. | 拓扑排序 【问题描述】 拓扑排序的流程如下: 1. 在有向图中选一个没有前驱的顶点并且输出之; 2. 从图中删除该顶点和所有以它为尾的弧。 重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。 采用邻接表存储有向图(可参考本次实验第2题代码),并通过栈来暂存所有入度为零的顶点。拓扑排序算法请参考教材上算法7.11和7.12或课件上的相应算法。 在本题中,读入一个有向图的信息(输入形式同本次实验第2题,详见下述【输入形式】),建立有向图并按照上述算法判断此图是否有回路,如果没有回路则输出拓扑有序的顶点数据序列。顶点的数据类型为int型。 注意:顶点编号从0开始。 【输入形式】仿照算法7.2的输入: 第1行输入图的结点个数n。第2行是图的各顶点数据。 之后的若干行(有向图为e行, 无向图为2e行) 依次输入各条边的弧尾、弧头的顶点编号。注意:因为算法7.2是按头插法建边链表的,所以要使得到的每个边链表中 都是按照邻接顶点的编号由小到大的顺序链接存储,就必须输入各边的顺序 首先是按照弧尾编号由小到大的顺序,并且输入弧尾相同的各边时 按照弧头顶点编号由大到小的顺序输入这些边,具体见样例输入。 最后一行输入-1和-1表示输入结束。 【输出形式】 如果读入的有向图含有回路,请输出“ERROR”,不包括引号。 如果读入的有向图不含有回路,请按照题目描述中的算法依次输出图的拓扑有序序列,每个整数后输出一个空格。 请注意行尾输出换行。 【样例输入】 0(空格)1(空格)2(空格)3 0(空格)1 1(空格)2 3(空格)2 -1(空格)-1 【样例输出】 3(空格)0(空格)1(空格)2(空格) 【说明】 本题中,需要严格的按照题目描述中的算法进行拓扑排序,并在排序的过程中将顶点数据先依次存储下来,直到最终能够判定有向图中不含回路之后,才能够进行输出,否则不能通过测试数据。 |
---|
#include<iostream>
#include<stack>
using namespace std;
typedef struct arcnode
{
int adjvex;
struct arcnode*nextarc;
int weight;
} arcnode;
typedef struct vertexnode
{
int data;
arcnode*firstarc;
}vertexnode;
typedef struct
{
int vexnum,arcnum;
vertexnode v[100];
}adjlist;
void creat(adjlist &s)
{
int x,y,k;
cin >> s.vexnum;
for(int i=0;i<s.vexnum;i++)
{
cin >> s.v[i].data;
//cout << s.a[i].weight;
s.v[i].firstarc=NULL;
}
for(k=0;k<50;k++)
{
cin >> x >> y;
if(x==-1&&y==-1)
break;
else
{
arcnode*p1;
p1=new arcnode;
p1->adjvex=y;
p1->nextarc=s.v[x].firstarc;
s.v[x].firstarc=p1;
}
}
}
int indegree[100];
int temp[100];
void Findid(adjlist &g)//求入度
{
int i;
arcnode*p;
for(i=0;i<g.vexnum;i++)
indegree[i]=0;
for(i=0;i<g.vexnum;i++)
{
p=g.v[i].firstarc;
while(p!=NULL)
{
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
void toposort(adjlist &g)//拓扑排序
{
stack<int> s;
Findid(g);
arcnode*p;
for(int i=0;i<g.vexnum;i++)//将入度为1的放进栈
{
if(indegree[i]==0)
s.push(i);
}
int count=0,i;
while(!s.empty())
{
i=s.top();
temp[count++]=s.top();//方便之后输出
s.pop();
p=g.v[i].firstarc;
while(p!=NULL)
{
int k=p->adjvex;
indegree[k]--;
if(indegree[k]==0)
s.push(k);
p=p->nextarc;
}
}
if(count<g.vexnum)
{
cout << "ERROR";
}
else
{
for (int i;i<count;i++)
cout << temp[i] <<" ";
}
}
int main()
{
adjlist s;
creat(s);
toposort(s);
}
3. | 关键路径求解 【问题描述】AOE网示意图若在带权的有向图中,以顶点表示事件,以有向边表示活动,边上的权值表示活动的开销(如该活动持续的时间),则此带权的有向图称为AOE网。如果用AOE网来表示一项工程,那么,仅仅考虑各个子工程之间的优先关系还不够,更多的是关心整个工程完成的最短时间是多少;哪些活动的延期将会影响整个工程的进度,而加速这些活动是否会提高整个工程的效率。因此,通常在AOE网中 列出完成预定工程计划所需要进行的活动,每个活动计划完成的时间,要发生哪些事件以及这些事件与某活动之间的关系,从而可以确定该项工程是否可行,来估算工程完成的时间以及确定哪些活动是影响工程进度的关键。 请参考《耿》教材的算法,编程求解上述问题。构建邻接表的函数,可以直接使用实验题“拓扑排序”中的代码。 【输入形式】第一行输入两个数字,依次是顶点数和边数;从第二行开始,输入边的信息,格式为(i j weight),i为弧尾顶点编号,j为弧头顶点编号,两个数据间用空格分隔;规定顶点编号从0开始。 【输出形式】关键活动的情况,格式为(顶点1 顶点2),两者间用1个空格分隔。要求:输出表示关键活动的各条弧时,按照弧尾顶点编号从小到大的顺序依次输出各条弧。 6 8 0 2 2 0 1 3 1 4 3 1 3 2 2 5 3 2 3 4 3 5 2 4 5 1 0 2 2 3 3 5 【样例说明】输入的(i j weight)时,不用输入括号。输出(顶点1 顶点2)时,不用输出括号。 |
---|
#include<iostream>
#include<stack>
using namespace std;
typedef struct arcnode
{
int adjvex;
struct arcnode*nextarc;
int weight;
} arcnode;
typedef struct vertexnode
{
int data;
arcnode*firstarc;
}vertexnode;
typedef struct
{
int vexnum,arcnum;
vertexnode v[100];
}adjlist;
void creat(adjlist &s)
{
int x,y,k;
int w;
cin >> s.vexnum >>s.arcnum;
for(int i=0;i<s.vexnum;i++)
{
//cin >> s.v[i].data;
//cout << s.a[i].weight;
s.v[i].firstarc=NULL;
}
for(k=0;k<s.arcnum;k++)
{
cin >> x >> y >>w;
arcnode*p1;
p1=new arcnode;
p1->adjvex=y;
p1->weight=w;
p1->nextarc=s.v[x].firstarc;
s.v[x].firstarc=p1;
}
}
int indegree[100];
int temp[100];
int count;
int vl[200],ve[200];
void Findid(adjlist &g)
{
int i;
arcnode*p;
for(i=0;i<g.vexnum;i++)
indegree[i]=0;
for(i=0;i<g.vexnum;i++)
{
p=g.v[i].firstarc;
while(p!=NULL)
{
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
void toposort(adjlist &g)//拓扑排序
{
stack<int> s;
Findid(g);
arcnode*p;
for(int i=0;i<g.vexnum;i++)
{
if(indegree[i]==0)
s.push(i);
}
count=0;
int i;
while(!s.empty())
{
i=s.top();
temp[count++]=s.top();
s.pop();
p=g.v[i].firstarc;
while(p!=NULL)
{
int k=p->adjvex;
indegree[k]--;
if(indegree[k]==0)
s.push(k);
if(ve[i]+p->weight>ve[k])
ve[k]=ve[i]+p->weight;
p=p->nextarc;
}
}
/* if(count<g.vexnum)
{
cout << "ERROR";
}
else
{
for (int i;i<count;i++)
cout << temp[i] <<" ";
}*/
}
void getpath(adjlist &g)
{
int top=count-1;
int in,out,i;
in=temp[top--];
for(i=0;i<g.vexnum;i++)
{
vl[i]=ve[in];
}
while(top!=-1)
{
in = temp[top--];
arcnode *p1=g.v[in].firstarc;
while(p1)
{
out=p1->adjvex;
if(vl[in]>vl[out]-p1->weight)
{
vl[in]=vl[out]-p1->weight;
}
p1=p1->nextarc;
}
}
for(in=0;in<g.vexnum;in++)
{
arcnode *p2=g.v[in].firstarc;
while(p2)
{
out=p2->adjvex;
int ee=ve[in];
int el=vl[out]-p2->weight;
if(ee==el)
cout << in <<" "<<out<<endl;
p2=p2->nextarc;
}
}
}
int main()
{
adjlist s;
creat(s);
toposort(s);
getpath(s);
return 0;
}
4. | 迪杰斯特拉算法求最短路径 【问题描述】 在带权有向图G中,给定一个源点v,求从v到G中的其余各顶点的最短路径问题,叫做单源点的最短路径问题。 在常用的单源点最短路径算法中,迪杰斯特拉算法是最为常用的一种,是一种按照路径长度递增的次序产生最短路径的算法。 请在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并参考《耿》教材中算法7.15,编程求出源点至图中每一个其它顶点的最短路径长度。 【输入形式】 输入的第一行依次是2个正整数n和s,表示图中共有n个顶点,且源点编号为s(顶点编号从0~n-1)。其中n不超过50,s小于n。 以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。当i和j相等的时候,对应的整数也是0。 【输出形式】 只有一行,共有n-1个整数,表示源点至其它每个顶点 (编号从小到大顺序) 各自的最短路径长度。如果不存在从源点至相应顶点的路径,则输出-1。 注意: 各输出数据间用1个空格分隔, 行尾输出换行。 【样例输入】 4 1 【样例输出】 6 4 7 【样例说明】 在本题中,需要在计算最短路径的过程中将每个顶点是否可达记录下来(可用非常大的整数来标记不可达),直到求出每个可达顶点的最短路径之后,算法才能够结束。 迪杰斯特拉算法的特点是按照路径长度递增的顺序,依次添加下一条长度最短的边,从而不断构造出相应顶点的最短路径。 |
---|
#include<iostream>
using namespace std;
#define WU 100000
typedef struct graph
{
int arc[100][100];
int weight;
int arcnum,vexnum;
}graph;
void creat(graph &g)
{
int i,j;
int temp;
for(i=0;i<g.vexnum;i++)
{
for(j=0;j<g.vexnum;j++)
{
cin >> temp;
if(temp!=0)
g.arc[i][j]=temp;
else
g.arc[i][j]=WU;
}
}
}
int dis[100];
int path[100];
int visited[100];
//数组path[n]是一个字符串。
//表示当前所找到的从始点v到终点vi的最短路径。
//若从v到vi有弧,则path[i]为vvi,否则为空串。
//数组dis[n]表示从当前所找到的从始点v到终点vi
//的最短路径的长度。
//visited[n]表示是否有过判断。
void dijkstra(graph &g)
{
int n=g.vexnum;
int v0=g.arcnum;
int i,j,k;
for(i=0;i<n;i++)//初始化
{
if(g.arc[v0][i]>0 && i!=v0)
{
dis[i]=g.arc[v0][i];
path[i]=v0;
}
else
{
dis[i]=WU;
path[i]=-1;
}
visited[i]=0;
}
path[v0]=v0;
dis[v0]=0;
visited[v0]=1;
for(i=1;i<n;i++)
{
int min=WU;
int u;
for(j=0;j<n;j++)
{
if(visited[j]==0 && dis[j]<min)
{
min = dis[j];
u=j;
}
}
visited[u]=1;
for(k=0;k<n;k++)
{
if(visited[k]==0 && g.arc[u][k]<WU && min+g.arc[u][k]<dis[k])
{
dis[k]=min+g.arc[u][k];
path[k]=u;
}
}
}
}
void print(graph g)
{
int i;
for(i=0;i<g.vexnum;i++)
{
if(dis[i])
{
if(dis[i]<WU)
{
cout << dis[i] <<" ";
}
else
{
cout << -1 <<" ";
}
}
}
}
int main()
{
graph g;
cin >> g.vexnum >> g.arcnum;
creat(g);
dijkstra(g);
print(g);
return 0;
}
5. | 弗洛伊德算法求最短路径 【问题描述】 对于下面一张若干个城市,以及城市之间距离的地图,请采用弗洛伊德算法求出所有城市之间的最短路径。 【输入形式】 顶点个数n,以及n*n的邻接矩阵,其中不可达使用9999表示。 【输出形式】 每两个顶点之间的最短路径和经过的顶点 注意:规定顶点自身到自身的dist值为0,此时path为该顶点编号。 【样例输入】 4 9999 4 11 9999 6 9999 2 9999 1 9999 9999 1 9999 3 9999 9999 【样例输出】 from(空格)0(空格)to(空格)0:(空格)dist(空格)=(空格)0(空格)path:0(换行) from 0 to 1: dist = 4 path:0 1 |
---|
#include<iostream>
#include<vector>
#define max 100
#define WU 9999
using namespace std;
int n;
int dist[max][max];
vector<int> path[max][max];
vector<int> Joinlist(int i,int k,int j)//合并路径
{
path[i][j].clear();
int temp = path[i][k].back();
path[i][k].pop_back();
vector<int> s;
int m,n;
for (m = 0; m < path[i][k].size(); m++)
{
s.push_back(path[i][k][m]);
}
for (n = 0; n < path[k][j].size(); n++)
{
s.push_back(path[k][j][n]);
}
path[i][k].push_back(temp);
return s;
}
void floyd()
{
for (int i = 0; i < n; i++)//初始化dist[i][j]与path[i][j]
{
for (int j = 0; j < n; j++)
{
cin >> dist[i][j];
if (dist[i][j] < WU)
{
path[i][j].push_back(i);
path[i][j].push_back(j);
}
else if(i==j&&dist[i][j]==WU)
{
path[i][j].push_back(i);
}
}
}
for (int k = 0; k < n; k++)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (dist[i][k] + dist[k][j] < dist[i][j]&&i!=j)
{
dist[i][j] = dist[i][k] + dist[k][j];
path[i][j] = Joinlist(i,k,j);
}
}
}
}
}
void print()
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (dist[i][j] == WU) dist[i][j] = 0;
cout << "from " <<i << " to " <<j <<":"
<<" dist = " << dist[i][j] << " path:";
for (int k = 0; k < path[i][j].size();k++)
{
cout << path[i][j][k] <<" ";
}
cout << endl;
}
}
}
int main()
{
cin >> n;
floyd();
print();
return 0;
}