将自己学的知识整合了一下,弄了个小的图论系统。
有关知识请看:http://blog.csdn.net/column/details/tulun.html
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<stack> #include<windows.h> using namespace std; typedef long long ll; const int oo=1e9;/**oo 表示无穷大*/ const int mm=1111111;/**mm 表示边的最大数量,记住要是原图的两倍,在加边的时候都是双向的*/ const int mn=2010;/**mn 表示点的最大数量*/ int head[mn]; int ip; int n,m;///n个点,m条边 int rd[mn],rdd[mn];///入度 int cd[mn];///出度 int dd[999][999],dist[mn];///任意两点距离 struct data { int to,w,next; } tu[mm]; void init()///初始化 { ip=0; memset(head,-1,sizeof(head)); memset(cd,0,sizeof(cd)); memset(rd,0,sizeof(rd)); memset(rdd,0,sizeof(rdd)); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) dd[i][j]=i==j?0:oo; } void add(int u,int v,int w)///添加边 { tu[ip].w=w,tu[ip].to=v,tu[ip].next=head[u],head[u]=ip++; } void input(int flag)///图的输入 { cout<<n<<"个点"<<m<<"条边"<<endl; cout<<"请输入这"<<m<<"条边以及他们的权值(注意这n个点的序列为1到"<<n<<"!)"<<endl; cout<<"示例输入:1 2 3(表示点1与点2有一条权值为3的边)"<<endl; for(int i=0; i<m; i++) { int a,b,c; cout<<"第"<<i+1<<"条边:"; cin>>a>>b>>c; while(a<1||a>n||b<1||b>n) { cout<<"请重新输入正确的顶点!!(1到"<<n<<"!)"<<endl; cin>>a>>b>>c; } if(flag) add(a,b,c); else add(a,b,c),add(b,a,c); rd[b]++; rdd[b]++; cd[a]++; } cout<<"建图完成!正在跳转…………"<<endl; Sleep(1000); system("cls"); } void floyd()///多源最短路 { system("cls"); for(int i=1; i<=n; i++) for(int j=head[i]; j!=-1; j=tu[j].next) dd[i][tu[j].to]=tu[j].w; for(int k=1; k<=n; k++) for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) dd[i][j]=min(dd[i][j],dd[i][k]+dd[k][j]); while(1) { cout<<"请输入任意两个点(1到"<<n<<")(输入两相同数字返回)"<<endl; int ss,ee; cout<<"请输入第一个点"; cin>>ss; while(ss<1||ss>n) { cout<<"请重新输入1到"<<n<<"之间的数"<<endl; cin>>ss; } cout<<"请输入第二个点"; cin>>ee; while(ee<1||ee>n) { cout<<"请重新输入1到"<<n<<"之间的数"<<endl; cin>>ee; } if(ss!=ee) { if(dd[ss][ee]<oo) cout<<"点"<<ss<<"到点"<<ee<<"之间的最短距离是"<<dd[ss][ee]<<endl<<endl; else cout<<"点"<<ss<<"无法到达点"<<ee<<endl<<endl; } else break; } } void spfa()///单源最短路 { system("cls"); while(1) { cout<<"请输入所求的点(1到"<<n<<",0表示返回)"<<endl; int s; cin>>s; while(s<0||s>n) { cout<<"请重新输入1到"<<n<<"之间的数"<<endl; cin>>s; } if(s==0)break; queue<int>q; for(int i=0; i<=n; i++) dist[i]=oo; int vis[mn]= {0}; q.push(s); dist[s]=0; while(!q.empty()) { int h=q.front(); q.pop(); vis[h]=0; for(int i=head[h]; i!=-1; i=tu[i].next) { int v=tu[i].to; int w=tu[i].w; if (dist[v]>dist[h]+w) { dist[v]=dist[h]+w; if (!vis[v]) { vis[v]=1; q.push(v); } } } } for(int i=1; i<=n; i++) if(i!=s) { if(dist[i]>=oo) cout<<s<<"点无法到达"<<i<<"点"<<endl; else cout<<s<<"点到"<<i<<"点的最短距离为"<<dist[i]<<endl; } } } int dfn[mn], low[mn];///dfn[]表示深搜的步数,low[u]表示u或u的子树能够追溯到的最早的栈中节点的次序号 int sccno[mn];///缩点数组,表示某个点对应的缩点值 int step; int scc_cnt;///强连通分量个数 vector<int> scc[mn];///得出来的缩点,scc[i]里面存i这个缩点具体缩了哪些点 stack<int> S; void dfs(int u) { dfn[u] = low[u] = ++step; S.push(u); for (int i = head[u]; i !=-1; i=tu[i].next) { int v = tu[i].to; if (!dfn[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if (!sccno[v]) low[u] = min(low[u], dfn[v]); } if (low[u] == dfn[u]) { scc_cnt += 1; scc[scc_cnt].clear(); while(1) { int x = S.top(); S.pop(); if (sccno[x] != scc_cnt) scc[scc_cnt].push_back(x); sccno[x] = scc_cnt; if (x == u) break; } } } void tarjan() { system("cls"); for(int i=0; i<=n; i++) scc[i].clear(); memset(sccno, 0, sizeof(sccno)); memset(dfn, 0, sizeof(dfn)); step = scc_cnt = 0; for (int i = 1; i <=n; i++) if (!dfn[i]) dfs(i); cout<<"该图的强连通分量有"<<scc_cnt<<"个,可以将原图缩成这"<<scc_cnt<<"个点"<<endl; for(int i=1; i<=scc_cnt; i++) { int l=scc[i].size(); for(int j=0; j<l; j++) cout<<scc[i][j]<<' '; cout<<"这"<<l<<"个点缩成点"<<i<<endl; } cout<<"输入任意字符返回"<<endl; step=getchar(); step=getchar(); } void youshow()///展示有向图功能 { cout<<"1.有向图的遍历"<<endl; cout<<"2.查看该有向图的拓扑排序序列"<<endl; cout<<"3.求出该有向图的最大流量"<<endl; cout<<"4.求所有点之间的最短路径"<<endl; cout<<"5.求一个点到其他点的最短路径"<<endl; cout<<"6.求出该有向图的强连通分量"<<endl; cout<<"0.退出并重新建图"<<endl; } void bianli()///有向图的遍历 { for(int i=1; i<=n; i++) for(int j=head[i]; j>=0; j=tu[j].next) cout<<i<<"--->"<<tu[j].to<<"的权值为"<<tu[j].w<<endl; } void topu()///拓扑排序 { queue<int>q; int flag=0; for(int i=1; i<=n; i++) if(!rdd[i]) q.push(i),flag=1; if(!flag) { cout<<"该有向图形成了环,无法进行拓扑排序"<<endl; return ; } cout<<"该有向图的拓扑排序序列为:"<<endl; while(!q.empty()) { int x=q.front(); cout<<x<<' '; rdd[x]--; q.pop(); for(int i=head[x]; i>=0; i=tu[i].next) { int to=tu[i].to; rdd[to]--; if(!rdd[to]) { q.push(to); } } } cout<<endl; for(int i=1; i<=n; i++) rdd[i]=rd[i]; } int node,src,dest,edge;/**node 表示节点数,src 表示源点,dest 表示汇点,edge 统计边数*/ int hea[mn],ver[mm],flow[mm],nex[mm]; int work[mn],dis[mn],q[mn]; void prepare(int _node, int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0; i<=node; ++i)hea[i]=-1; edge=0; } void addedge( int u, int v, int c) { ver[edge]=v,flow[edge]=c,nex[edge]=hea[u],hea[u]=edge++; ver[edge]=u,flow[edge]=0,nex[edge]=hea[v],hea[v]=edge++; } bool Dinic_bfs() { int i,u,v,l,r=0; for(i=0; i<node; ++i)dis[i]=-1; dis[q[r++]=src]=0; for(l=0; l<r; ++l) for(i=hea[u=q[l]]; i>=0; i=nex[i]) if(flow[i]&&dis[v=ver[i]]<0) { dis[q[r++]=v]=dis[u]+1; if(v==dest) return 1; } return 0; } int Dinic_dfs( int u, int exp) { if(u==dest) return exp; for( int &i=work[u],v,tmp; i>=0; i=nex[i]) if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0) { flow[i]-=tmp; flow[i^1]+=tmp; return tmp; } return 0; } int Dinic_flow() { int i,ret=0,delta; while(Dinic_bfs()) { for(i=0; i<node; ++i)work[i]=hea[i]; while((delta=Dinic_dfs(src,oo)))ret+=delta; } return ret; } void Dinic()///求最大流 { prepare(n+2,0,n+1); for(int i=1; i<=n; i++) { if(!rd[i]) addedge(0,i,1e9); if(!cd[i]) addedge(i,n+1,1e9); } for(int i=1; i<=n; i++) for(int j=head[i]; j!=-1; j=tu[j].next) addedge(i,tu[j].to,tu[j].w); cout<<"该图能通过的最大流量为"<<Dinic_flow()<<endl; } void youxiangtu()///有向图 { system("cls"); init(); input(1); while(1) { system("cls"); youshow(); cout<<"请输入对应数字:(0到6!):"<<endl; int tt; while(cin>>tt) { while(tt<0||tt>6) { cout<<"请重新输入0到6之间的数字!!"<<endl; cin>>tt; } if(!tt) return; else if(tt==1) bianli(); else if(tt==2) topu(); else if(tt==3) Dinic(); else if(tt==4) { floyd(); break; } else if(tt==5) { spfa(); break; } else { tarjan(); break; } } } } void wushow()///展示无向图功能 { cout<<"1.无向图的遍历"<<endl; cout<<"2.求所有点之间的最短路径"<<endl; cout<<"3.求一个点到其他点的最短路径"<<endl; cout<<"4.求出该无向图的最小生成树"<<endl; cout<<"5.求该图的强连通分量"<<endl; cout<<"0.退出并重新建图"<<endl; } void wsbianli()///无向图的深度遍历 { cout<<"该图的深度遍历序列是:"<<endl; bool vis[mn]= {0}; stack<int>ss; ss.push(1); while(!ss.empty()) { int x=ss.top(); cout<<x<<' '; ss.pop(); vis[x]=1; for(int i=head[x]; i!=-1; i=tu[i].next) { int to=tu[i].to; if(!vis[to]) ss.push(to),vis[to]=1; } } cout<<endl<<endl; } void wgbianli()///无向图的广度遍历 { cout<<"该图的广度遍历序列是:"<<endl; bool vis[mn]= {0}; queue<int>q; q.push(1); while(!q.empty()) { int x=q.front(); cout<<x<<' '; q.pop(); vis[x]=1; for(int i=head[x]; i!=-1; i=tu[i].next) { int to=tu[i].to; if(!vis[to]) q.push(to),vis[to]=1; } } cout<<endl<<endl; } void wbianli()///无向图的遍历 { system("cls"); while(1) { cout<<"请选择遍历方式(输入0到2):"<<endl; cout<<"0.返回 1.深度遍历 2.广度遍历"<<endl; int tt; cin>>tt; while(tt>2||tt<0) { cout<<"请重新输入0到2!!"<<endl; cin>>tt; } if(tt==1) wsbianli(); else if(tt==2) wgbianli(); else break; } } struct tree { int a,b,w; void sett(int x,int y,int z) { a=x,b=y,w=z; } } ttu[mm]; bool cmp(tree a,tree b) { return a.w<b.w; } int f[mn]; int fa(int x) { if(f[x]!=x) return f[x]=fa(f[x]); return f[x]; } void mintree()///无向图的最小生成树 { system("cls"); int bj=0; for(int i=1; i<=n; i++) { f[i]=i; for(int j=head[i]; j!=-1; j=tu[j].next) ttu[bj++].sett(i,tu[j].to,tu[j].w); } sort(ttu,ttu+bj,cmp); cout<<"该无向图的最小生成树的所有边为:"<<endl; int sum=0,s=0; for(int i=0; i<bj; i++) { int x1=fa(ttu[i].a),x2=fa(ttu[i].b); if(x1!=x2) { cout<<ttu[i].a<<"--->"<<ttu[i].b<<"权值为"<<ttu[i].w<<endl; f[x1]=fa(f[x2]); s++; sum+=ttu[i].w; } if(s==n-1) break; } cout<<"该最小生成树的总权值为"<<sum<<endl; cout<<"输入任意字符返回"<<endl; s=getchar(); s=getchar(); } void wuxiangtu()///无向图 { init(); input(0); while(1) { system("cls"); wushow(); cout<<"请输入对应数字:(0到5!):"<<endl; int tt; while(cin>>tt) { while(tt<0||tt>5) { cout<<"请重新输入0到5之间的数字!!"<<endl; cin>>tt; } if(!tt) return; else if(tt==1) { wbianli(); break; } else if(tt==2) { floyd(); break; } else if(tt==3) { spfa(); break; } else if(tt==4) { mintree(); break; } else { tarjan(); break; } } } } int main() { while(1) { system("cls"); cout<<"----------这是一个图论小系统----------"<<endl; cout<<"请输入要建图的点数(输入0结束程序):"; cin>>n; if(!n)break; cout<<"请输入要建图的边数:"; cin>>m; cout<<"正在跳转…………"<<endl; Sleep(800); system("cls"); cout<<"请输入对应数字(返回输入0):"<<endl; cout<<"1.有向图 2.无向图"<<endl; int tt; while(cin>>tt) { if(tt==1) { youxiangtu(); system("cls"); break; } else if(tt==2) { wuxiangtu(); break; } else if(tt==0) { system("cls"); break; } cout<<"请输入0,1,2!"<<endl; } } return 0; }
时间: 2024-10-12 03:22:40