自主学习四.实验设计
实验题目:图的邻接矩阵计算
设计方案:
设有向图D=<V,E>,V={v1,v2,…vn}。
如:给定有向图D=<V,E>,
V={a,b,c,d},
E={<a,a>,<a,b>,<a,b>,<a,d>,<d,c>,<c,d>,<c,b>}.
一、需求分析
程序应满足如下功能:
1.能由有向图转换为对应的邻接矩阵。
2.能计算邻接矩阵A,A ²,A ³…A ⁿ.
3.图的邻接矩阵可用来求点到点之间的通路条数。所以程序应能求出点到点之间不同长度的通路条数。
4.能求出点到自身的不同长度的回路条数。
5.能求出长度小于等于n的通路数
6.能求出长度小于等于n的回路数
二、概要设计
1.输入有向图D,将其转换为对应的邻接矩阵。
有向图的输入应包括V,E。可用一维数组存储V;E在后面的计算中不用,一个字符串存储足矣;二维数组存储邻接矩阵。由输入的E与V的关系计算点到点之间的通路数(即邻接矩阵中相应位置的元素)
2.计算邻接矩阵A,A ²,A ³…A ⁿ.
用矩阵乘法算法可求出A,A ²,A ³…A ⁿ.
3.根据A,A ²,A ³…A ⁿ输出所求。
根据矩阵中相应元素的求和等计算可得出所求。
三、详细设计
1.制定输入如下:
图1.1
2.制定菜单如下:
图1.2
3.邻接矩阵的转换算法:
for(int i=0;i<iSize;i++){//遍历E中有向边。
if(E[i]=='<'){//“<”后元素为起始点
for(intj=0;j<n;j++){//遍历V,查找起始点
if(V[j]==E[i+1]){//找到起始点,获取到起始点下标j
for(intk=0;k<n;k++){//遍历V,查找终点
if(V[k]==E[i+3]){//找到终点,获取到终点下标k
A1[j][k]++;//邻接矩阵相应位置+1
}
}
}
}
i+=4;
}
}
2. 计算邻接矩阵A,A ²,A ³…A ⁿ.
这里并不把每一个邻接矩阵A ⁿ都存储下来。根据所需长度求出A ⁿ.
2.1.矩阵乘法算法实现:
//矩阵乘法
void MatMul(int **a,int **b,int **c,intn){
for(inti=0;i<n;i++){
for(intj=0;j<n;j++){
*((int*)c+n*i+j)=0;
for(intk=0;k<n;k++){
*((int*)c+n*i+j)+=(*((int *)a+n*i+k))*(*((int *)b+n*k+j));
}
}
}
}
2.2 A ⁿ的计算:
分析如下:
A ²=A*A
A ³= A ²*A
.
.
.
A ⁿ=A(n-1)*A //(n-1)为上标,不能理解学数学的写报告时一堆公式的感受
每次把A(n-1)的计算结果存储为result。
将result复制到临时存储temp
求出A ⁿ
算法实现:
for(int i=2;i<=len;i++){
MatMul((int**)A1,(int **)temp,(int **)result,n);
copy((int**)temp,(int **)result,n);
}
temp=result这里包装成函数,方便调用。算法如下:
//二维数组复制
void copy(int **a,int **b,int n){
for(inti=0;i<n;i++){
for(intj=0;j<n;j++){
*((int*)a+n*i+j)=*((int *)b+n*i+j);
}
}
}
3.通路数的计算
点到点之间长度为n的通路(回路)数为矩阵中相应位置元素。
设B=A+A ²+A ³+…+A ⁿ,长度小于等于n的通路为B中各元素之和。
回路为对角线之和。
定义如下变量:
int sum=0;//总和
int partSum=0;//每个A ⁿ矩阵和
int diagonalSum=0;//对角线总和
int diaPartSum=0;//每个A ⁿ矩阵对角线和
算法实现:
for(A~ A ⁿ)
partSum=sum(A ⁿ);
sum+=partSum;
diagonalSum+=diaPartSum;
求每个矩阵所有元素之和函数sum()如下:
//矩阵所有元素之和及对角线之和
void Sum(int **a,int *partSum,int*diaPartSum,int n){
*partSum=0;
*diaPartSum=0;
for(inti=0;i<n;i++){
(*diaPartSum)+=*((int*)a+n*i+i);
for(intj=0;j<n;j++){
(*partSum)+=*((int*)a+n*i+j);
}
}
}
四、程序源代码
程序源码为C++语言编写,编译环境为dev-cpp.
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
/* run this program using the consolepauser or add your own getch, system("pause") or input loop */
void display(int **a,int n){
for(inti=0;i<n;i++){
for(intj=0;j<n;j++){
cout<<*((int*)a+n*i+j)<<" ";
}
cout<<endl;
}
}
//二维数组复制
void copy(int **a,int **b,int n){
for(inti=0;i<n;i++){
for(int j=0;j<n;j++){
*((int*)a+n*i+j)=*((int *)b+n*i+j);
}
}
}
//矩阵所有元素之和及对角线之和
void Sum(int **a,int *partSum,int*diaPartSum,int n){
*partSum=0;
*diaPartSum=0;
for(inti=0;i<n;i++){
(*diaPartSum)+=*((int*)a+n*i+i);
for(intj=0;j<n;j++){
(*partSum)+=*((int*)a+n*i+j);
}
}
}
//矩阵乘法
void MatMul(int **a,int **b,int **c,intn){
for(inti=0;i<n;i++){
for(intj=0;j<n;j++){
*((int*)c+n*i+j)=0;
for(intk=0;k<n;k++){
*((int*)c+n*i+j)+=(*((int *)a+n*i+k))*(*((int *)b+n*k+j));
}
}
}
}
int main(int argc, char** argv) {
int n,m;
cout<<"设有向图D=<V,E>:"<<endl;
cout<<"请输入顶点个数:";
cin>>n;
charV[n];//V
intA1[n][n];//矩阵
//初始化A1
for(inti=0;i<n;i++){
for(intj=0;j<n;j++){
A1[i][j]=0;
}
}
//输入D
stringE;
cout<<"请输入V:";
for(inti=0;i<n;i++){
cin>>V[i];
}
cout<<"请输入E:"<<endl;
cin>>E;
//求D的邻接矩阵
unsignedint iSize=E.length();
for(inti=0;i<iSize;i++){//遍历E中有向边。
if(E[i]=='<'){//"<"后元素为起始点
for(intj=0;j<n;j++){//遍历V,查找起始点
if(V[j]==E[i+1]){//找到起始点,获取到起始点下标j
for(intk=0;k<n;k++){//遍历V,查找终点
if(V[k]==E[i+3]){//找到终点,获取到终点下标k
A1[j][k]++;//邻接矩阵相应位置+1
}
}
}
}
i+=4;
}
}
cout<<"*************************************************"<<endl;
cout<<"1.求长度为n的矩阵"<<endl;
cout<<"2.求长度为n的通路(回路)"<<endl;
cout<<"3.求长度小于等于n的通路(回路)"<<endl;
cout<<"0.退出"<<endl;
cout<<"*************************************************"<<endl;
cout<<"请选择:";
intoption;
cin>>option;
while(option){
intlen;
cout<<"请输入长度n:";
cin>>len;
inttemp[n][n];
copy((int**)temp,(int **)A1,n);
intresult[n][n];
copy((int**)result,(int **)A1,n);
intsum=0;//总和
intpartSum=0;//每个A(n)矩阵和
intdiagonalSum=0;//对角线总和
intdiaPartSum=0;//每个A(n)矩阵对角线和
Sum((int**)A1,&sum,&diagonalSum,n);
for(inti=2;i<=len;i++){
MatMul((int**)A1,(int **)temp,(int **)result,n);
copy((int**)temp,(int **)result,n);
Sum((int**)result,&partSum,&diaPartSum,n);
sum+=partSum;
diagonalSum+=diaPartSum;
}
charstart;
charend;
switch(option){
option=0;
case1:
display((int**)result,n);
break;
case2:
cout<<"请输入起始点:";
cin>>start;
fflush(stdin);
cout<<"请输入终点:";
cin>>end;
for(inti=0;i<n;i++){
if(V[i]==start){//获取到起始点下标
for(intj=0;j<n;j++){
if(V[j]==end){//获取到终点下标
stringtempStr="的通路有";
if(start==end){
tempStr="的回路有";
}
cout<<"D中"<<start<<"到"<<end
<<"长度为"<<len<<tempStr<<result[i][j]<<"条。" <<endl;
}
}
}
}
break;
case3:
cout<<"D中长度小于等于" <<len<<"的通路有"<<sum
<<"条,其中有"<<diagonalSum<<"条回路。"<<endl;
break;
}
cout<<"请选择:";
cin>>option;
}
return0;
}
五、调试运行:
1.求长度为n的矩阵:
图1.3
2.求长度为n的通路:
图1.4
3.求长度为n的回路:
图1.5
4.求长度小于等于n的通路(回路):
图1.6
5.输入0正常退出程序。
经过多组测试,结果符合实验需求,其余测试结果略。
注意事项:
图1.7
如图,因V的存储采用的字符数组形式,输入V时应输入单个字符,输入E时不应有空格等作为分隔符,有向边用<vi,vj>形式表示。
六、实验总结(略)