c++数据结构:图

图 Graph

图的ADT定义
G = (Vertx顶点, Edge边(无向)、弧(单向,有弧头,弧尾)) 多对多
邻接 a->f则f是a的邻接点,反之不成立
出度、入度
有向图、无向图

稀疏图、稠密图 边少or边多 极致的稠密图是完全图
带权图
子图G’=(V’, E’)
路径P=(v0v1…vn-1)
回路R=(v0v1…vn-1v0)

图的存储

邻接矩阵/邻接表

邻接矩阵

是顺序表 o(n2)
包含结点数目,结点名称,邻接矩阵

邻接表

是链表,使用前插法o(1),适合于存储稀疏图o(e),e是边数,完全图e=n2而且还占用空间更多
包含边(权重,邻接点,指针),顶点(信息,弧链头指针),邻接表(顶点数目,顶点集)
dfs:递归/非递归(栈) 类似前序遍历 邻接矩阵和邻接表的深度优先遍历序不同
bfs:层次遍历 queue

连通性

判断无向图的连通性:一趟dfs,若全走完则图连通
连通分支数:走几趟dfs
有向图的连通性:强连通分量(任意两顶点都可达)
深度优先遍历完成序+反向深度优先遍历

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<string>
#include<queue>
using namespace std;
#define ll long long
#define MAX_SIZE 100
//邻接矩阵
struct Graph{
    int vexNumber; //节点数
    char vexInfo[MAX_SIZE];//结点信息
    int adjMatrix[MAX_SIZE][MAX_SIZE];//邻接矩阵
};
//输出邻接矩阵
void printGraph(const Graph &G){
   for(int i=0;i<G.vexNumber;i++){
       for(int j=0;j<G.vexNumber;j++){
           cout<<G.adjMatrix[i][j]<<" ";
       }
       cout<<endl;
   }
}
//邻接矩阵递归dfs
void DFS1(const Graph &G,int v0,int visited[]){
    cout<<G.vexInfo[v0]<<" ";
    visited[v0]=1;
    for(int i=0;i<G.vexNumber;i++){
        if(visited[i]==0&&G.adjMatrix[v0][i]==1)DFS1(G,i,visited);
    }
}
void DFS1(const Graph &G){
    int visited[G.vexNumber]={0};
    for(int i=0;i<G.vexNumber;i++){
        if(visited[i]==0)DFS1(G,i,visited);
    }
    cout<<endl;
}
//邻接矩阵非递归dfs
int findAdjVex(const Graph& G,int v0,int visited[]){
    int adj=-1;
    for(int i=0;i<G.vexNumber;i++){
        if(G.adjMatrix[v0][i]==1&&visited[i]==0){
            adj=i;
            break;
        }
    }
    return adj;
}
string DFS(const Graph &G,int v0,int visited[]){
    string result="";
    stack<int> S;
    int p=v0;
    while(p>=0 || !S.empty()){
        while(p>=0){
            visited[p]=1;
            result +=G.vexInfo[p]+" ";
            S.push(p);
            p=findAdjVex(G,p,visited);
        }
        if(!S.empty()){
            int q=S.top();
            p=findAdjVex(G,q,visited);
            if(p==-1)S.pop();
        }
    }
    return result;
}
string DFS(const Graph &G){
    string result="";
    int visited[G.vexNumber]={0};
    for(int i=0;i<G.vexNumber;i++){
        if(visited[i]==0)result+=DFS(G,i,visited);
    }
    return result;
}
//邻接矩阵bfs
void BFS(const Graph &G){
    int visited[G.vexNumber]={0};
    int p=0;
    queue<int> q;
    q.push(p);
    while(!q.empty()){
        p=q.front();
        q.pop();
        cout<<G.vexInfo[p]<<" ";
        visited[p]=1;
        for(int i=0;i<G.vexNumber;i++){
            if(visited[i]==0&&G.adjMatrix[p][i]==1){
                q.push(i);
                visited[i]=1;
            }
        }
    }
}
//邻接矩阵dfs完成序
string DFS_finished(const Graph &G,int v0,int visited[]){
    string result="";
    stack<int> S;
    int p=v0;
    while(p>=0||!S.empty()){
        while(p>=0&&!visited[p]){
            S.push(p);
            visited[p]=1;
            p=findAdjVex(G,p,visited);
        }
        if(!S.empty()){
            p=S.top();
            int q=findAdjVex(G,p,visited);
            if(q==-1){
                result+=G.vexInfo[p];
                p=-1;
                S.pop();
            }else{
                p=q;
            }
        }
    }
    return result;
}
string DFS_finished(const Graph &G){
    string result="";
    int visited[G.vexNumber]={0};
    for(int i=0;i<G.vexNumber;i++){
        if(visited[i]==0)result+=DFS_finished(G,i,visited);
    }
    return result;
}
//邻接表 这里加了一个指向的结点名称,code才是目标结点序号,得根据需要自行更改
//因为写题的时候一时间没想到在输入结点名称的时候遍历找一下结点序号
//(其实oj上的题的结点名称都是abcd按顺序排的
struct ArcNode{
    int weight;
    char adj;
    int code;
    ArcNode *nextarc;
};
struct VexNode{
    char Info;
    ArcNode *firstarc;
};
struct linkGraph{
    VexNode *vexes;
    int vexnumber;
};
//邻接表初始化
int InitGraph(linkGraph &G,int vexnumber){
    G.vexes=new VexNode[vexnumber];
    G.vexnumber=vexnumber;
    for(int i=0;i<vexnumber;i++){
        G.vexes[i].firstarc=NULL;
    }
    return 0;
} 
//邻接矩阵->邻接表 这里的adj是目标结点序号
void Graph2linkGraph(linkGraph &G,const Graph &g){
    G.vexnumber=g.vexNumber;
    G.vexes=new VexNode[G.vexnumber];
    for(int i=0;i<G.vexnumber;i++){
        G.vexes[i].Info=g.vexInfo[i];
        G.vexes[i].firstarc=nullptr;
    }
    for(int i=0;i<G.vexnumber;i++){
        for(int j=0;j<G.vexnumber;j++){
            if(g.adjMatrix[i][j]!=0){
                ArcNode *node=new ArcNode();
                node->adj=j;
                node->nextarc=nullptr;
                if(G.vexes[i].firstarc==nullptr)G.vexes[i].firstarc=node;
                else{
                    node->nextarc=G.vexes[i].firstarc;
                    G.vexes[i].firstarc=node;
                }

            }
        }
    }
}
//销毁邻接表 这是无code版
int DestroylinkGraph(linkGraph &G){
    for(int i=0;i<G.vexnumber;i++){
        while(G.vexes[i].firstarc!=NULL){
            ArcNode *p=G.vexes[i].firstarc;
            G.vexes[i].firstarc=p->nextarc;
            delete p;
        }
    }
    delete[]G.vexes;
    G.vexes=NULL;
    G.vexnumber=0;
    return 0;
}
//输出邻接表 这是有code版
void PrintlinkGraph(const linkGraph &G){
    for(int i=0;i<G.vexnumber;i++){
        cout<<G.vexes[i].Info;
        for(ArcNode *p=G.vexes[i].firstarc;p!=nullptr;p=p->nextarc){
            cout<<" --> "<<G.vexes[p->code].Info;
        }
        cout<<endl;
    }
}
//直接输入邻接表 用a-g-f这种方式输入
void InputlinkGraph1(linkGraph &LG){
    cin>>LG.vexnumber;
    LG.vexes=new VexNode[LG.vexnumber];
    for(int i=0;i<LG.vexnumber;i++)LG.vexes[i].firstarc=nullptr;
    for(int i=0;i<LG.vexnumber;i++){
        char tmp;
        cin>>tmp;
        LG.vexes[i].Info=tmp;
        scanf("%c",&tmp);
        while(tmp=='-'){
            cin>>tmp;
            ArcNode *p=new ArcNode();
            p->adj=tmp;
            p->nextarc=nullptr;
            if(LG.vexes[i].firstarc==nullptr){
                LG.vexes[i].firstarc=p;
            }
            else {
                ArcNode *t=LG.vexes[i].firstarc;
                while(t->nextarc!=nullptr)t=t->nextarc;
                t->nextarc=p;
            }
            scanf("%c",&tmp);
        }
    }
    for(int i=0;i<LG.vexnumber;i++){
        for(ArcNode *p=LG.vexes[i].firstarc;p!=nullptr;p=p->nextarc){
            for(int j=0;j<LG.vexnumber;j++){
                if(p->adj==LG.vexes[j].Info){p->code=j;break;}
            }
        }
    }
}
//邻接表转邻接矩阵 有code版
void linkGraph2Graph(const linkGraph &LG,Graph &G){
    G.vexNumber=LG.vexnumber;
    for(int i=0;i<G.vexNumber;i++)G.vexInfo[i]=LG.vexes[i].Info;
    for(int i=0;i<G.vexNumber;i++){
        for(ArcNode *p=LG.vexes[i].firstarc;p!=nullptr;p=p->nextarc){
            G.adjMatrix[i][p->code]=1;
        }
    }
}


邻接表存储图的广度优先遍历

要求输出字典序最小的答案
输入

7 9 5
0 3
0 2
0 4
3 1
3 2
4 5
1 5
2 5
5 6

邻接表的adj得用int来存结点的标号
输入邻接表的时候不能用前插法因为题目要求输出字典序最小的答案

//邻接矩阵
struct Graph{
    int vexNumber; //节点数
    int vexInfo[MAX_SIZE];//结点信息
    int adjMatrix[MAX_SIZE][MAX_SIZE];//邻接矩阵
};
struct ArcNode{
    int weight;
    char adj;
    int code;
    ArcNode *nextarc;
};
struct VexNode{
    int Info;
    ArcNode *firstarc;
};
struct linkGraph{
    VexNode *vexes;
    int vexnumber;
};
void BFS(const linkGraph &G,int x){
    int visited[G.vexnumber]={0};
    queue<int> q;
    q.push(x);
    while(!q.empty()){
        int v=q.front();q.pop();
        visited[v]=1;
        cout<<G.vexes[v].Info<<" ";
        for(ArcNode *p=G.vexes[v].firstarc;p!=nullptr;p=p->nextarc){
            if(!visited[p->code]){
                q.push(p->code);
                visited[p->code]=1;
            }
        }
    }
}

int main(){
    Graph G;
    cin>>G.vexNumber;
    int m,x;
    cin>>m>>x;
    if(G.vexNumber!=0&&m!=0){
    for(int i=0;i<G.vexNumber;i++)G.vexInfo[i]=i;
    for(int i=0;i<G.vexNumber;i++){
        for(int j=0;j<G.vexNumber;j++)G.adjMatrix[i][j]=0;
    }
    while(m--){
        int a,b;
        cin>>a>>b;
        G.adjMatrix[a][b]=1;
        G.adjMatrix[b][a]=1;
    }
    linkGraph LG;
    Graph2linkGraph(LG,G);
    //PrintlinkGraph(LG);
    BFS(LG,x);
    }else cout<<"0";
    return 0;
}

DFS应用-计算可达城市对

每次oj交题基本都狂打补丁,最后代码变得冗长又低效,可能这就是shit山的诞生8
这里用邻接表存可能好一点,样例里面有2000*2000,邻接矩阵的数组开在struct里面会RE,所以偷懒开了个全局数组

int m[2001][2001];

//邻接矩阵
struct Graph{
    int vexNumber; //节点数
    int vexInfo[MAX_SIZE];//结点信息
    int adjMatrix[MAX_SIZE][MAX_SIZE];//邻接矩阵
};
int findAdjVex(const Graph& G,int v0,int visited[]){
    int adj=-1;
    for(int i=0;i<G.vexNumber;i++){
        if(m[v0][i]==1&&visited[i]==0){
            adj=i;
            break;
        }
    }
    return adj;
}
string DFS(const Graph &G,int v0,int visited[]){
    string result="";
    stack<int> S;
    int p=v0;
    while(p>=0 || !S.empty()){
        while(p>=0){
            visited[p]=1;
            result +=G.vexInfo[p]+" ";
            S.push(p);
            p=findAdjVex(G,p,visited);
        }
        if(!S.empty()){
            int q=S.top();
            p=findAdjVex(G,q,visited);
            if(p==-1)S.pop();
        }
    }
    return result;
}
int DFS(const Graph &G){
    string result="";
    int visited[G.vexNumber]={0};
    int cnt=0;
    for(int i=0;i<G.vexNumber;i++){
        if(visited[i]==0)result+=DFS(G,i,visited);
        for(int i=0;i<G.vexNumber;i++)if(visited[i]==1)cnt++;
        for(int i=0;i<G.vexNumber;i++)visited[i]=0;
    }
    
    
    return cnt;
    //return result;
}
int main(){
    Graph G;
    cin>>G.vexNumber;
    int n;//路数
    cin>>n;
    for(int i=0;i<G.vexNumber;i++)G.vexInfo[i]=i+1;
    while(n--){
        int a,b;
        cin>>a>>b;
        m[a-1][b-1]=1;
    }
    ll res=0;
    res=DFS(G);
    cout<<res;
    return 0;
}

欧拉回路的判断

1.无向图连通<-一趟dfs可以走过所有结点
2.所有结点度为偶数

string DFS(const Graph &G,int v0,int visited[]){
    string result="";
    stack<int> S;
    int p=v0;
    while(p>=0 || !S.empty()){
        while(p>=0){
            visited[p]=1;
            result +=G.vexInfo[p]+" ";
            S.push(p);
            p=findAdjVex(G,p,visited);
        }
        if(!S.empty()){
            int q=S.top();
            p=findAdjVex(G,q,visited);
            if(p==-1)S.pop();
        }
    }
    return result;
}
bool DFS(const Graph &G){
    string result="";
    int visited[G.vexNumber]={0};
    bool f=1;//连通
    for(int i=0;i<G.vexNumber;i++){
        if(visited[i]==0)result+=DFS(G,i,visited);
        for(int i=0;i<G.vexNumber;i++)if(visited[i]==0)f=0;//不连通
        for(int i=0;i<G.vexNumber;i++)visited[i]=0;
        if(f==1)break;
    }
    
    
    return f;
    //return result;
}
int main(){
    Graph G;
    cin>>G.vexNumber;
    int n;//路数
    cin>>n;
    for(int i=0;i<G.vexNumber;i++)G.vexInfo[i]=i+1;
    while(n--){
        int a,b;
        cin>>a>>b;
        m[a-1][b-1]=1;
        m[b-1][a-1]=1;
    }
    bool flag=0;
    flag=DFS(G); //判断图是连通的
    //所有结点度为偶数
    int flag2=1;
    for(int i=0;i<G.vexNumber;i++){
        int dv=0;
        for(int j=0;j<G.vexNumber;j++)if(m[i][j])dv++;
        if(dv%2==1){
            flag2=0;
            break;
        }
    }
    if(flag==1&&flag2==1)cout<<"1";
    else cout<<"0";
    return 0;
}


地下迷宫探索

完全可以用递归做,不要给自己找事

void DFS_migong(const Graph &G,int v0,int visited[]){
    res[k]=v0+1;k++;
    visited[v0]=1;
    for(int i=0;i<G.vexNumber;i++){
        if(visited[i]==0&&m[v0][i]==1){
            DFS_migong(G,i,visited);
            res[k]=v0+1;k++;
        }
    }
}
int main(){
    Graph G;
    cin>>G.vexNumber;
    int n;//路数
    cin>>n;
    int x;
    cin>>x;
    for(int i=0;i<G.vexNumber;i++)G.vexInfo[i]=i+1;
    while(n--){
        int a,b;
        cin>>a>>b;
        m[a-1][b-1]=1;
        m[b-1][a-1]=1;
    }
    int visited[G.vexNumber]={0};
    DFS_migong(G,x-1,visited);
    for(int i=0;i<k;i++)cout<<res[i]<<" ";
    int c=0;
    for(int i=0;i<G.vexNumber;i++)if(visited[i])c++;
    if(c!=G.vexNumber)cout<<"0";
    return 0;
}

拯救007

用bfs,注意考虑跳出情况

bool visited[101];
int n;
double d;
struct node{
    double x,y;
    double distance;
}a[101];
void bfs(){
    queue<node> q;
    for(int i=0;i<n;i++){
        if(a[i].distance<=d){
            q.push(a[i]);
            visited[i]=1;
        }
    }
    while(q.size()){
        node t=q.front();q.pop();
        if(t.x+d>=50||t.x-d<=-50||t.y+d>=50||t.y-d<=-50){
            cout<<"Yes";return;
        }
        for(int i=0;i<n;i++){
            if(visited[i]==0&&sqrt((t.y-a[i].y)*(t.y-a[i].y)+(t.x-a[i].x)*(t.x-a[i].x))<=d){
                q.push(a[i]);
                visited[i]=1;
            }
        }
    }
    printf("No");
    return;
}
int main(){
    cin>>n>>d;
    for(int i=0;i<n;i++){
        cin>>a[i].x>>a[i].y;
        a[i].distance=sqrt(a[i].x*a[i].x+a[i].y*a[i].y)-7.5;//距离岛边距离
    }
    if(d>=50-7.5)cout<<"Yes"<<endl;
    else{
        bfs();
    }
    return 0;
}

拓扑排序

输出拓扑排序/判断图是否有环

//计算邻接矩阵的出度和入度
void CalculateDegree(const Graph &g,int inDegree[],int outDegree[]){
    for(int i=0;i<g.nodeNumber;i++){
        int c=0;
        for(int j=0;j<g.nodeNumber;j++){
            if(g.adjMatrix[i][j])c++;
        }
        outDegree[i]=c;
    }
    for(int j=0;j<g.nodeNumber;j++){
        int c=0;
        for(int i=0;i<g.nodeNumber;i++){
            if(g.adjMatrix[i][j])c++;
        }
        inDegree[j]=c;
    }
}
//邻接矩阵的拓扑排序
void TopoSort(Graph &g,int inDegree[]){
    stack<int> s;
    for(int i=0;i<g.nodeNumber;i++){
        if(!inDegree[i])s.push(i);
    }
    int res[g.nodeNumber]={0};int cnt=0;
    while(!s.empty()){
        int t=s.top();s.pop();
        res[cnt]=t;
        cnt++;
        for(int i=0;i<g.nodeNumber;i++){
            if(g.adjMatrix[t][i]){
                inDegree[i]--;
                if(inDegree[i]==0)s.push(i);
            }
        }
    }
    if(cnt<g.nodeNumber)cout<<"YES"<<endl;
    else{
        // for(int i=0;i<g.nodeNumber;i++){
        //     cout<<res[i]<<" ";
        // }
        // cout<<endl;
        cout<<"NO"<<endl;
    }
}
//邻接表的拓扑排序
void TopologicalSort(LinkGraph &G,int inDegree[]){
    stack<int> s;
    int cnt=0,k;
    ArcNode *p;
    for(int i=0;i<G.vexNumber;i++){
        if(inDegree[i]==0)s.push(i);
    }
    int res[G.vexNumber]={0};
    while(s.empty()==0){
        int t=s.top();
        s.pop();
        res[cnt]=t;
        cnt++;
        for(p=G.vexes[t].firstArc;p!=nullptr;p=p->next){
            k=p->adj;
            inDegree[k]--;
            if(inDegree[k]==0)s.push(k);
        }
    }
    if(cnt<G.vexNumber)cout<<"ERROR"<<endl;
    else{
        for(int i=0;i<G.vexNumber;i++){
            cout<<res[i]<<" ";
        }
        cout<<endl;
    }
}
//输入二元组输出图是否有环
int main(){
    while(1){
    Graph g;
    cin>>g.nodeNumber;
    int T;
    cin>>T;
    if(g.nodeNumber==0&&T==0)break;
    int inDegree[g.nodeNumber]={0},outDegree[g.nodeNumber]={0};
    for(int i=0;i<g.nodeNumber;i++){
        g.vexInfo[i]=i+1;
    }
    while(T--){
        int a,b;
        cin>>a>>b;
        g.adjMatrix[a-1][b-1]=1;
    }
    CalculateDegree(g,inDegree,outDegree);
    TopoSort(g,inDegree);
    }
    return 0;
}

是否为有效的拓扑序列

bool judge(Graph &g,int topo[],const int inDegree[]){
    bool flag=1;
    int in[g.nodeNumber];
    for(int i=0;i<g.nodeNumber;i++)in[i]=inDegree[i];
    for(int i=0;i<g.nodeNumber;i++){
        int tmp=topo[i];
        if(in[tmp]){flag=0;break;}
        for(int j=0;j<g.nodeNumber;j++){
            if(g.adjMatrix[tmp][j])in[j]--;
        }
    }
    return flag;
}

关键路径

#include<iostream>
#include<stack>
#include<vector>
using namespace std;
#define maxsize 101
struct graph{//邻接矩阵 
	int vexNumber;//顶点数量
	int ve[maxsize],vl[maxsize];//每个顶点的最早和最晚 
	int vexInfo[maxsize];
	int adjMatrix[maxsize][maxsize];
};
void inputGraph(graph &g){ //邻接矩阵存储有向无环带权图 
	cin>>g.vexNumber;
	for(int i=0;i<g.vexNumber;i++){g.vexInfo[i]=i;}
	int edgeNumber;
	cin>>edgeNumber;
	for(int i=0;i<edgeNumber;i++){
		int a,b,w;
		cin>>a>>b>>w;
		g.adjMatrix[a][b]=w;
	}
}
void printGraph(const graph& g){
	cout<<"vex number:"<<g.vexNumber<<endl;
//	cout<<"vex info:";
//	for(int i=0;i<g.vexNumber;i++)cout<<g.vexInfo[i];
//	cout<<endl;
	for(int i=0;i<g.vexNumber;i++){
		for(int j=0;j<g.vexNumber;j++){
			cout<<g.adjMatrix[i][j]<<" ";
		}
		cout<<endl;
	}
}
void KeyPath(graph &G){
	int indegree[G.vexNumber]={0};
	vector<int> res;
	for(int i=0;i<G.vexNumber;i++){
		for(int j=0;j<G.vexNumber;j++){
			if(G.adjMatrix[i][j]==1)indegree[j]++;
		}
	}
	stack<int> S;//用队列也可以但是输出顺序会不同 
	for(int i=0;i<G.vexNumber;i++){
		if(!indegree[i])S.push(i);
	}
	while(!S.empty()){
		int tmp=S.top();
		//cout<<tmp;
		res.push_back(tmp);
		S.pop();
		for(int i=0;i<G.vexNumber;i++){
			if(G.adjMatrix[tmp][i]==1){
				indegree[i]--;
				if(indegree[i]==0)S.push(i);
			}
			
		}
	}
	for(int i=0;i<G.vexNumber;i++)G.ve[i]=0; //求每个结点最早ve 
	for(int i=0;i<res.size();i++){
		int v=res[i];
		for(int j=0;j<G.vexNumber;j++){
			if(G.adjMatrix[j][v]!=0){
				if(G.ve[v]==0||G.ve[v]<G.ve[j]+G.adjMatrix[j][v])
					G.ve[v]=G.ve[j]+G.adjMatrix[j][v];
			}
		}
	}
	//求每个结点的最晚vl 
	for(int i=0;i<G.vexNumber;i++)G.vl[i]=G.ve[res[G.vexNumber-1]];
	for(int i=res.size()-1;i>=0;i--){
		int v=res[i];
		for(int j=0;j<G.vexNumber;j++){
			if(G.adjMatrix[v][j]!=0){
				if(G.vl[v]==G.ve[res[G.vexNumber-1]]||G.vl[v]>G.vl[j]-G.adjMatrix[v][j])
					G.vl[v]=G.vl[j]-G.adjMatrix[v][j];
			}
		}
	}
	//行优先输出关键路径
	for(int i=0;i<G.vexNumber;i++){
		for(int j=0;j<G.vexNumber;j++){
			if(G.adjMatrix[i][j]){
				int e=G.ve[i];
				int l=G.vl[j]-G.adjMatrix[i][j];
				if(e==l){//是关键路径 
					cout<<i<<" --> "<<j<<":"<<e<<endl;
				}
			}
		}
	}
	
}
int main(){
	graph G;
	inputGraph(G);
	//printGraph(G);
	KeyPath(G);
	return 0;
}

边输入优先输出关键路径

int T;
struct Graph{
    int nodeNumber;
    int ve[MAX_SIZE],vl[MAX_SIZE];
    int vexInfo[MAX_SIZE];
    int adjMatrix[MAX_SIZE][MAX_SIZE];
};
struct linshi{
    int qidian,zhongdian,wei;
};
void keyPath(Graph &G,int inDegree[],linshi array[]){
    vector<int> res;//拓扑顺序
    stack<int> s;
    for(int i=0;i<G.nodeNumber;i++)if(!inDegree[i])s.push(i);
    while(!s.empty()){
        int t=s.top();s.pop();
        res.push_back(t);
        for(int i=0;i<G.nodeNumber;i++){
            if(G.adjMatrix[t][i]){
                inDegree[i]--;
                if(inDegree[i]==0)s.push(i);
            }
        }
    }
    //求最早发生ve
    for(int i=0;i<G.nodeNumber;i++)G.ve[i]=0;
    for(int i=0;i<res.size();i++){
        int v=res[i];
        for(int j=0;j<G.nodeNumber;j++){
            if(G.adjMatrix[j][v]!=0){
                if(G.ve[v]==0||G.ve[v]<G.ve[j]+G.adjMatrix[j][v])
                    G.ve[v]=G.ve[j]+G.adjMatrix[j][v];
            }
        }
    }
    // for(int i=0;i<G.nodeNumber;i++){
    //     cout<<G.ve[i]<<endl;
    // }
    //求最晚发生vl
    for(int i=0;i<G.nodeNumber;i++){
        G.vl[i]=G.ve[res[G.nodeNumber-1]];
    }
    for(int i=res.size()-1;i>=0;i--){
        int v=res[i];
        for(int j=0;j<G.nodeNumber;j++){
            if(G.adjMatrix[v][j]){
                if(G.vl[v]==G.ve[res[G.nodeNumber-1]]||G.vl[v]>G.vl[j]-G.adjMatrix[v][j])
                    G.vl[v]=G.vl[j]-G.adjMatrix[v][j];
            }
        }
    }
    // for(int i=0;i<G.nodeNumber;i++){
    //     cout<<G.vl[i]<<endl;
    // }
    for(int i=0;i<T;i++){
        int a=array[i].qidian;
        int b=array[i].zhongdian;
        //cout<<G.ve[a]<<endl;
        //cout<<G.vl[b]-G.adjMatrix[a][b]<<endl;
        int e=G.ve[a];
        int l=G.vl[b]-G.adjMatrix[a][b];
        if(e==l)cout<<a<<"-->"<<b<<":"<<e<<endl;
    }
}
int main(){
    Graph g;
    cin>>g.nodeNumber;
    
    cin>>T;
    int inDegree[g.nodeNumber]={0},outDegree[g.nodeNumber]={0};
    for(int i=0;i<g.nodeNumber;i++){
        g.vexInfo[i]=i;
    }
    linshi array[T];
    for(int i=0;i<T;i++){
        int a,b,w;
        cin>>array[i].qidian>>array[i].zhongdian>>array[i].wei;
        a=array[i].qidian;
        b=array[i].zhongdian;
        w=array[i].wei;
        g.adjMatrix[a][b]=w;
    }
    CalculateDegree(g,inDegree,outDegree);
    keyPath(g,inDegree,array);
    return 0;
}

最短工期

这个代码写得有点烂

bool TopoSort(Graph &g,int inDegree[],int topo[]){
    bool flag=0;
    stack<int> s;
    for(int i=0;i<g.nodeNumber;i++){
        if(!inDegree[i])s.push(i);
    }
    int cnt=0;
    while(!s.empty()){
        int t=s.top();s.pop();
        topo[cnt]=t;
        cnt++;
        for(int i=0;i<g.nodeNumber;i++){
            if(g.adjMatrix[t][i]){
                inDegree[i]--;
                if(inDegree[i]==0)s.push(i);
            }
        }
    }
    if(cnt<g.nodeNumber){flag=0;cout<<"Impossible"<<endl;}
    else flag=1;
    return flag;
}
void keyPath(Graph &G,int inDegree[],linshi array[]){
    vector<int> res;//拓扑顺序
    stack<int> s;
    for(int i=0;i<G.nodeNumber;i++)if(!inDegree[i])s.push(i);
    while(!s.empty()){
        int t=s.top();s.pop();
        res.push_back(t);
        for(int i=0;i<G.nodeNumber;i++){
            if(G.adjMatrix[t][i]){
                inDegree[i]--;
                if(inDegree[i]==0)s.push(i);
            }
        }
    }
    int max=0;
    //求最早发生ve
    for(int i=0;i<G.nodeNumber;i++)G.ve[i]=0;
    for(int i=0;i<res.size();i++){
        int v=res[i];
        for(int j=0;j<G.nodeNumber;j++){
            if(G.adjMatrix[j][v]!=0){
                if(G.ve[v]==0||G.ve[v]<G.ve[j]+G.adjMatrix[j][v])
                    {G.ve[v]=G.ve[j]+G.adjMatrix[j][v];
                    if(G.ve[v]>max)max=G.ve[v];
                    }
            }
        }
    }
    cout<<max<<endl;
}
int main(){
    Graph g;
    cin>>g.nodeNumber;
    
    cin>>T;
    int inDegree[g.nodeNumber]={0},outDegree[g.nodeNumber]={0};
    for(int i=0;i<g.nodeNumber;i++){
        g.vexInfo[i]=i;
    }
    linshi array[T];
    for(int i=0;i<T;i++){
        int a,b,w;
        cin>>array[i].qidian>>array[i].zhongdian>>array[i].wei;
        a=array[i].qidian;
        b=array[i].zhongdian;
        w=array[i].wei;
        g.adjMatrix[a][b]=w;
    }
    CalculateDegree(g,inDegree,outDegree);
    int inDegree2[g.nodeNumber]={0};
    for(int i=0;i<g.nodeNumber;i++)inDegree2[i]=inDegree[i];
    int topo[g.nodeNumber]={0};
    bool flag=TopoSort(g,inDegree,topo);
    if(flag==1)keyPath(g,inDegree2,array);
    return 0;
}

最小生成树MST

是原图的极小连通子图,包含n个结点且边的权重之和最小
性质:U是顶点的一个非空子集,另外一个集合是V-U,他们之间有一些直达边,所有边的最小的一条必然在最小生成树之内

Prim(普里姆)算法

U是最小生成树的结点集合,初始U只有一个顶点,挑选与该集合内的结点相连的权重最小的边加入结果树并将顶点合并到U中,直到V-U为空,U包含所有结点时算法结束
构造数组cost[0…n-1]表顶点vi到U的最短边的长度,adj[0…n-1]表vi到U的最短边在U中的邻接点的下标
每次从cost中选出值最小的顶点vk,将<vk,vadj[k]>加入生成树中,将vk并入u中;修正V-U中各顶点的cost和adj值

以0为U中首个结点计算cost和adj值的代码实例

int cost[G.nodeNumber]={-1},adj[G.nodeNumber]={-1};
    fer(i,0,G.nodeNumber){
        cost[i]=-1;
        adj[i]=-1;
    }
    fer(i,0,G.nodeNumber){
        if((G.adjMatrix[0][i])){
            cost[i]=G.adjMatrix[0][i];
            adj[i]=0;
        }
    }

Prim算法求最小生成树代码

int prim(Graph &G){
    bool visited[G.nodeNumber]={0};
    int cost[G.nodeNumber]fer(i,0,G.nodeNumber){cost[i]=INF;}
    fer(i,0,G.nodeNumber){
        if(G.adjMatrix[0][i])cost[i]=G.adjMatrix[0][i];
    }
    visited[0]=1;cost[0]=0;
    int now,min;
    int sum=0;
    fer(circle,0,G.nodeNumber-1){
        now=min=INF;
        fer(j,0,G.nodeNumber){
            if(visited[j]==0){
                if(cost[j]<min){
                    now=j;
                    min=cost[j];
                }
            }
        }
        if(now==INF)break;
        sum+=min;
        visited[now]=1;
        fer(i,0,G.nodeNumber){
        //重点在于此处如何更新cost
            if(visited[i]==0&&G.adjMatrix[now][i]<cost[i])cost[i]=G.adjMatrix[now][i];
        }
    }
    return sum;
}

Kruskal (克鲁斯卡尔) 算法

将所有边按权值从小到大排,每个顶点是一个独立的点集,依次扫描每条边,若vi,vj不在同一点集中,则合并两者并加入生成树,否则舍弃该边,输出n-1条边时结束

对所有边按权重排序后输出。如果图只有1个点(即没有边),则直接输出空行。
注意题目的测试数据构造时用的排序算法如下:
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(a[j]<a[i]) swap(i,j); // 交换

int main(){
    Graph G;
    cin>>G.nodeNumber;
    fer(i,0,G.nodeNumber)G.vexInfo[i]=i;
    int kcnt=0;
    fer(i,0,G.nodeNumber){
        fer(j,0,G.nodeNumber){
            cin>>G.adjMatrix[i][j];
            if(G.adjMatrix[i][j]!=0)
            {
                k[kcnt].a=i;k[kcnt].b=j;
                k[kcnt].w=G.adjMatrix[i][j];
                kcnt++;}
            }
    }
    fer(i,0,kcnt){
        fer(j,i+1,kcnt){
            if(k[j].w<k[i].w)swap(k[i],k[j]);
        }
    }
    fer(i,0,kcnt){
        printf("<%d,%d>:%d\n",k[i].a,k[i].b,k[i].w);
    }
    return 0;
}

判断是否在同一点集&合并点集可用:

并查集

静态树
fa表示父节点

//初始化 将父节点设为自己 也可以将父节点的fa设为设为负的子节点个数
int fa[MAXN];
inline void init(int n)
{
    for (int i = 1; i <= n; ++i)
        fa[i] = i;
}
//查询(递归)
int find(int x)
{
    if(fa[x] == x)
        return x;
    else
        return find(fa[x]);
}
//合并 将前者的父节点设为后者的父节点
inline void merge(int i, int j)
{
    fa[find(i)] = find(j);
}
//优化合并 路径压缩
int find(int x)
{
    if(x == fa[x])
        return x;
    else{
        fa[x] = find(fa[x]);  //父节点设为根节点
        return fa[x];         //返回父节点
    }
}
int find(int x)
{
    return x == fa[x] ? x : (fa[x] = find(fa[x]));
}

在这里插入图片描述

最短路径问题

Dijstra(迪杰斯特拉)算法

单源起点的最短路径问题

在这里插入图片描述
算法:
在这里插入图片描述

只有一行,共有n-1个整数,表示源点至其它每一个顶点的最短路径长度。如果不存在从源点至相应顶点的路径,输出-1。 请注意行尾输出换行。

#define INF 99999999 
int main(){
    Graph G;
    cin>>G.nodeNumber;
    int origin;
    cin>>origin;
    fer(i,0,G.nodeNumber)G.vexInfo[i]=i;
    fer(i,0,G.nodeNumber){
        fer(j,0,G.nodeNumber)cin>>G.adjMatrix[i][j];
    }
    bool visited[G.nodeNumber]={0};
    int shortest[G.nodeNumber],path[G.nodeNumber];
    fer(i,0,G.nodeNumber){ 
    	//数组赋初值本来用的是memset,对字节赋初值inf应为0x3f,判断要写0x3f3f3f3f,折腾半天wa了两发
        shortest[i]=INF;path[i]=INF;
    }
    visited[origin]=1;
    fer(i,0,G.nodeNumber){
        if(G.adjMatrix[origin][i]!=0){
            shortest[i]=G.adjMatrix[origin][i];
        }
    }
    fer(circle,0,G.nodeNumber){
        int minn=INF,mini=-1;
        fer(i,0,G.nodeNumber){
            if(shortest[i]<minn&&visited[i]==0){
                minn=shortest[i];
                mini=i;
            }
        }
        //找到最短路径,加入u集合,记录到path中
        visited[mini]=1;
        path[mini]=minn;
        //更新shortest
        fer(i,0,G.nodeNumber){//遍历每一个没有走过的顶点
            if(visited[i]==0){
                fer(j,0,G.nodeNumber){
                   //借助已走过的结点走
                   if(visited[j]==1&&G.adjMatrix[j][i]!=0
                      &&G.adjMatrix[j][i]+shortest[j]<shortest[i]){
                            shortest[i]=G.adjMatrix[j][i]+shortest[j];
                        }
                    
                }
            }
        }
    }
    fer(i,0,G.nodeNumber){
        if(i!=origin){
            if(path[i]!=INF)cout<<path[i]<<" ";
            else cout<<"-1"<<" ";
        }
    }
    return 0;
}

Floyd(弗洛伊德)算法

任意两点间最短路径,可以用多次Dijstra算法求,但Floyd算法更适合
在这里插入图片描述
在这里插入图片描述
Floyd算法输出最短路径包含的边

struct floyd{
    int len,v;
};
	int n;
    cin>>n;
    struct floyd f[n][n];
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>f[i][j].len>>f[i][j].v;
        }
    }
    int p,q;
    cin>>p>>q;
    cout<<f[p][q].len<<endl;
    int path[n]={-1};
    for(int i=0;i<n;i++)path[i]=-1;
    path[0]=p;
    path[n-1]=q;
    int i=2;
    while(f[p][path[n-i+1]].v!=p){
        path[n-i]=f[p][path[n-i+1]].v;
        i++;
    }
    for(int i=0;i<n;i++){
        if(path[i]!=-1)cout<<path[i]<<" ";
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值