图论模板
2017-04-30 20:57
176 查看
//TheWaySoFar 图论模板 一.最短路 1.Dijkstra算法(邻接矩阵/邻接表) 2.SPFA 3.Bellman-ford 4.folyd 5.次短路 6.K短路(Astart + SPFA) 二.分图 1.染色体判二分 2.匈牙利算法 三.拓扑排序 1.模板(邻接表/邻接矩阵) 四.并查集(简单略) 五.最小生成树 1.prim(邻接表/邻接矩阵) 六.网络流 1.FF 2.EK(紫书略) 3.Dinic 七.杂 1.强连通分量tarjan算法 2.连通图的割点 3.欧拉回路(Fleury算法) 4.哈密顿回路(回溯) 5.判断图的可图性(Havel-Hakimi定理) ----------------------------------------------------------------------------------- 以下模板多数的头模板 #include <iostream> #include <cstdio> #include <sstream> #include <cstring> #include <map> #include <set> #include <vector> #include <stack> #include <queue> #include <algorithm> #include <cmath> #define LL long long #define INF 0x3f3f3f3f #define maxn 1024 #define Pair pair<int, int> #define mem(a, b) memset(a, b, sizeof(a)) #define _ ios_base::sync_with_stdio(0),cin.tie(0) //freopen("1.txt", "r", stdin); using namespace std; --------------------------------------------------------------------------------- 一:最短路 1.dijstra算法 /* void dijkstra(int s) 无向图 邻接表 + 优先队列 参数 s : 出发点 */ 代码: typedef pair<int, int> P; struct edge { int to, cost; }; int V; vector<edge> G[maxn]; int d[maxn]; void dijkstra(int s) { priority_queue<Pair, vector<Pair>, greater<Pair> > que; fill(d, d + V + 2, INF); d[s] = 0; que.push(Pair(0, s)); while (!que.empty()) { Pair p = que.top(); que.pop(); int v = p.second; if (d[v] < p.first) continue;//到v点的距离如果已经被更新 这无须执行以下操作 for (int i = 0; i < G[v].size(); ++i) { edge e= G[v][i]; if (d[e.to] > d[v] +e.cost) { d[e.to] = d[v] + e.cost; que.push(P(d[e.to], e.to)); } } } } int main() { int m; cin >> V >> m; for (int i = 0; i < m; ++i) { int from, to, cost; cin >> from >> to >> cost; edge var; var.to = to; var.cost = cost; G[from].push_back(var); var.to = from; G[to].push_back(var);//无向图 } dijkstra(1); for (int i = 1; i <= V; ++i) cout << d[i] << endl; //到i的距离 return 0; } /* dijstra算法 邻接矩阵 */ int map[MAXV][MAXV],d[MAXV]; bool vis[MAXV]; //是否已经访问拿过 int n,m,x; void dijkstra(){ int i,j,v,mi; for(i=1;i<=n;i++){ vis[i]=0; d[i]=map[x][i]; } for(i=1;i<=n;i++){ mi=inf; for(j=1;j<=n;j++) if(!vis[j] && d[j]<mi){ //寻找没有访问过且距离已被更新为最小的点 v=j; mi=d[j]; } vis[v]=1; for(j=1;j<=n;j++){ if(!vis[j] && map[v][j]+d[v]<d[j]) d[j]=map[v][j]+d[v]; } } for(int i=1;i<=n;i++) cout<<d[i]<<endl; } int main(){ freopen("d.txt","r",stdin); int i,a,b,c,j; while(~scanf("%d%d%d",&n,&m,&x)){ for(i=1;i<=n;i++){ for(j=1;j<=n;j++) if(i!=j) map[i][j]=inf; else map[i][j]=0; } for(i=1;i<=m;i++){/ scanf("%d%d%d",&a,&b,&c); map[a][b]=c; map[b][a]=c; } dijkstra(); } return 0; } --------------------------------------------------------------------------------------------- 2.SPFA算法 /* bool SPFA(int source) 返回是否存在负环 参数:source:出发点 queue + 邻接表 */ const int maxn = 5000; typedef struct node { int to, cost; }edge; vector<edge> v[maxn]; int in_sum[maxn]; //每个点的入队次数 int in_que[maxn]; //是否在队列里面 int n, m, d[maxn]; bool SPFA(int source) { deque<int> q; for (int i = 1; i <= n; ++i) { d[i] = i == source ? 0 : INF; } q.push_back(source); in_sum[source]++; in_que[source] = 1; while (!q.empty()) { int curr = q.front(); q.pop_front(); in_que[curr] = 0; for (int i = 0; i < v[curr].size(); ++i) { int to = v[curr][i].to; if (d[curr] < INF && d[to] > d[curr] + v[curr][i].cost) { d[to] = d[curr] + v[curr][i].cost; if (in_que[to] == 0) { in_que[to] = 1; if(++in_sum[to] == n) //入队次数等于n则存在负环 return false; } if(!q.empty()) { if(d[to] > d[q.front()]) q.push_back(to); else q.push_front(to); }else q.push_back(to); } } } return true; } ------------------------------------------------------------------------------- 3.Bellman-ford /* bool Bellman_Ford() 返回是否存在负环 */ typedef struct Edge //边 { int u, v; int cost; }Edge; Edge edge ; int dis , pre ; bool Bellman_Ford() { for(int i = 1; i <= nodenum; ++i) //初始化 dis[i] = (i == original ? 0 : MAX); for(int i = 1; i <= nodenum - 1; ++i) for(int j = 1; j <= edgenum; ++j) if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~) { dis[edge[j].v] = dis[edge[j].u] + edge[j].cost; pre[edge[j].v] = edge[j].u; } bool flag = 1; //判断是否含有负权回路 for(int i = 1; i <= edgenum; ++i) if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost) { flag = 0; break; } return flag; } ------------------------------------------------------------------------------------ 4.folyd /* O(n^3); */ /*---算法区---*/ for(k=1; k<=n; k++) for(i=1; i<=n; i++) for(j=1; j<=n; j++) { if(d[i][k]+d[k][j]<d[i][j]) d[i][j]=d[i][k]+d[k][j]; } /*---算法区---*/ ------------------------------------------------------------------------------------- 5.次短路 /*见白书*/ ---------------------------------------------------------------------------------- 6.K短路 /* A*算法:使用估值函数来进行搜索,f(n)=g(n)+h(n), 其中f(n)表示状态起点经过状态n到状态终点的估值, g(n)为状态起点到状态n的距离值, h(n)为状态n到状态终点的距离值。 SPFA的意义在于反着求其t点到各点的最短距离 */ #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <queue> using namespace std; const int maxn=1005; const int maxe=100005; struct State{ int f; // f=g+dis dis表示当前点到终点的最短路径,即之前的预处理 int g; //g表示到当前点的路径长度 int u; bool operator<(const State b)const{ if(f==b.f)return g>b.g; return f>b.f; } }; struct Edge{ int v; int w; int ne; }edge[maxe],reedge[maxe]; int head[maxn],rehead[maxn]; int dis[maxn],vis[maxn]; int n,m; int cot; int s,t,k; void init(){ cot=0; //cot代表边的id 每条边的id都不相同 memset(head,-1,sizeof(head)); memset(rehead,-1,sizeof(rehead)); } void addedge(int u,int v,int w){ edge[cot].v=v; edge[cot].w=w; edge[cot].ne=head[u];//记录上一次u的id是多少 这样方便遍历 初始值为-1 head[u]=cot;//head[u] 给这个u标记上独一无二的id reedge[cot].v=u; reedge[cot].w=w; reedge[cot].ne=rehead[v]; rehead[v]= 4000 cot++; } void SPFA(){ queue<int>q; memset(vis,0,sizeof(vis)); memset(dis,-1,sizeof(dis)); int u,v; q.push(t); vis[t]=true;//vis表示当前点是否在队列 dis[t]=0; while(!q.empty()){ u=q.front(); q.pop(); //rehead[u] 是u最后一次出现的id reedge[i].ne 代表第i次出现的边上一次出现的id for(int i=rehead[u];~i;i=reedge[i].ne){ //~i取反 当i为-1时正好取反为0 退出for v=reedge[i].v; if(dis[v]>dis[u]+reedge[i].w||dis[v]==-1){ dis[v]=dis[u]+reedge[i].w; if(!vis[v]){ q.push(v); vis[v]=true; } } } vis[u]=false; } } int Astart(){ if(s==t)k++; if(dis[s]==-1)return -1; int cnt=0; priority_queue<State>q; // 优先队列 State a,b; a.g=0; a.u=s; a.f=a.g+dis[a.u]; q.push(a); while(!q.empty()){ b=q.top(); q.pop(); if(b.u==t){ cnt++; //printf("%d %d %d %d\n",b.f,b.g,b.u,dis[b.u]); if(cnt==k)return b.g; } for(int i=head[b.u];~i;i=edge[i].ne){ a.g=b.g+edge[i].w; a.u=edge[i].v; a.f=a.g+dis[a.u]; q.push(a); } } return -1; } int main() { int u,v,w; while(scanf("%d %d",&n,&m)==2){ init(); for(int i=0;i<m;i++){ scanf("%d %d %d",&u,&v,&w); addedge(u,v,w); } scanf("%d %d %d",&s,&t,&k); SPFA(); /* for(int i=1;i<=n;i++){ printf("%d:%d\n",i,dis[i]); } */ printf("%d\n",Astart()); } return 0; } ---------------------------------------------------------------------------------- 二.分图 1.染色体判二分 /* 利用BFS */ bool f = true; for (int i = 1; i <= n; ++i) { //if(vis[i]) == 1; if(!vis[i]) { vis[i] = 1; que.push(i); } while (!que.empty()) { int x = que.front(); que.pop(); for (int i = 0; i < vec[x].size(); ++i) { int temp = vec[x][i]; if (vis[temp] == vis[x]) { f = false; break; } if(vis[temp] == 0) { vis[temp] = 3 - vis[x]; que.push(temp); } } } } -------------------------------------------------------------------------------------- 2.匈牙利算法 /* bool dfs(int u) 返回是否匹配成功 邻接矩阵模板 */ constint MAXN=1000; int uN,vN; //u,v数目 int g[MAXN][MAXN];//编号是0~n-1的 int linker[MAXN]; bool used[MAXN]; bool dfs(int u) { int v; for(v=0;v<vN;v++) if(g[u][v]&&!used[v]) { used[v]=true; if(linker[v]==-1||dfs(linker[v])) { linker[v]=u; return true; } } return false; } int hungary() { int res=0; int u; memset(linker,-1,sizeof(linker)); for(u=0;u<uN;u++) { memset(used,0,sizeof(used)); if(dfs(u)) res++; } return res; } ---------------------------------------------------------------------------------------------------- /* 邻接表模板 匈牙利算法 参数:x:当前匹配的人 */ #define maxn 1024 #define Pair pair<int, int> #define mem(a, b) memset(a, b, sizeof(a)) #define _ ios_base::sync_with_stdio(0),cin.tie(0) using namespace std; vector<int> vec[maxn]; bool used[maxn]; bool line[maxn][maxn]; int find(int x) { for (int i = 0; i < vec[x].size(); ++i) { int v = vec[x][i]; if (!used[v]) { used[v] = true; if (obj[v] == 0 && find(obj[v])) { obj[v] = x; return true; } } } return false; } int main() { int ans = 0; for (int i = 1; i <= n; ++i) { memset(used, 0, sizeof(used)); if(find(i)) ans += 1; } } -------------------------------------------------------------------------------------------- 三.拓扑排序 /* int topsort() 返回是可以排序:0是有环,1是yes 优先队列 */ int n, m; vector<int> g[maxn]; //图 vector<int> ans; //答案 int in[maxn]; //入度 int topsort() { int sum = 0; priority_queue<int, vector<int>, greater<int> > que; //每次弹出最小的 按照字典序记录答案 for (int i = 1; i <= n; ++i) { if (in[i] == 0) que.push(i); //把入度为0的入队 } while ( !que.empty() ) { int x = que.top(); sum++; //记录入度为0的点数量 ans.push_back(x); //记录答案 que.pop(); for (int j = 0; j < g[x].size(); ++j) { int v = g[x][j]; if (--in[v] == 0) { //入度减去1 是否已经为零 que.push(v); //为零则入队 } } } if (sum < n) return 0; 度为0的数量小于总的点数 有环存在 return 1; } --------------------------------------------------------------------------------------------- 五.最小生成树 1.prim(邻接表/邻接矩阵) /* void prim(int s) 参数 */ using namespace std; int g[maxn][maxn]; int n, m, ans; int vis[maxn], pre[maxn], pos, lowcost[maxn]; priority_queue<Pair, vector<Pair>, greater<Pair> > que; void prim(int s) //由某点开始 { int ans = 0; for (int i = 1; i <= n; ++i) { lowcost[i] = g[s][i]; // 到此点的距离 pre[i] = 1; //此点的前驱, que.push(Pair(g[s][i], i)); } lowcost[s] = 0; vis[s] = -1; int mini = INF, pos = -1; while (!que.empty()) { mini = INF; Pair v = que.top(); que.pop(); //如果这个点被访问continue注:这也是最后优先队列还有很多点 却可以退出的原因, 因为其都被标-1 if(vis[v.second] == -1) continue; if(g[pre[v.second]][v.second]) printf("%d %d\n", pre[v.second], v.second); //输出搭建的边 ans += lowcost[v.second]; vis[v.second] = -1; for (int j = 1; j <= n; ++j) { if (vis[j] != -1 && g[pre[j]][j] > g[v.second][j]) { lowcost[j] = g[v.second][j]; //更新到此点的距离 que.push(Pair(g[v.second][j], j)); pre[j] = v.second; //更新此点的前区 } } } //cout << ans << endl; //输出总消费 } -------------------------------------------------------------------------------- //邻接表建图 using namespace std; vector<Pair> vec[maxn]; int lowcost[maxn], pre[maxn], vis[maxn]; priority_queue<Pair, vector<Pair>, greater<Pair> > que; priority_queue<Pair, vector<Pair>, greater<Pair> > ans; int n, m, sum, MAX; void prim() { mem(lowcost, INF); for (int i =0; i < vec[1].size(); ++i) { Pair temp = vec[1][i]; lowcost[temp.second] = temp.first; pre[temp.second] = 1; que.push(temp); } lowcost[1] = 0; vis[1] = 1; while (!que.empty()) { Pair v = que.top(); que.pop(); if(vis[v.second]) continue; sum += v.first; MAX = max(MAX, v.first); vis[v.second] = 1; ans.push(Pair(pre[v.second], v.second)); for (int i = 0; i < vec[v.second].size(); ++i) { Pair temp = vec[v.second][i]; if (!vis[temp.second] && temp.first < lowcost[temp.second]) { lowcost[temp.second] = temp.first; pre[temp.second] = v.second; que.push(temp); } } } cout << MAX << endl; cout << sum << endl; while (!ans.empty()) { cout << ans.top().first << " " << ans.top().second << endl; ans.pop(); } } ------------------------------------------------------------------------------------------------ 六.网络流 1.FF /* int max_flow(int s,int t) 返回最大流量 参数: s:起点 t:终点 数组模拟邻接表建图 */ #include <cstdio> #include <cstring> #include <iostream> #include <string> #include <algorithm> #include <map> #include <vector> using namespace std; const int N = 1100; const int INF = 0x3f3f3f3f; struct Node { int to;//终点 int cap; //容量 int rev; //反向边 }; vector<Node> v ; bool used ; void add_Node(int from,int to,int cap) //重边情况不影响 { v[from].push_back((Node) { to,cap,v[to].size() //v[to].size() 因为接下来要把这条表插入v[to] 所以这里用v[to].size() //正好v[i][j],j是由下标0开始的,size条边就是其反向边 }); v[to].push_back((Node) { from,0,v[from].size()-1 //反过来要减一也很好理解了 }); } int dfs(int s,int t,int f) { if(s==t) return f; used[s]=true; for(int i=0; i<v[s].size(); i++) { Node &tmp = v[s][i]; //注意 这个地方是为了改变v[s][i]的大小 所以加了引用 if(used[tmp.to]==false && tmp.cap>0) { int d=dfs(tmp.to,t,min(f,tmp.cap)); if(d>0) { tmp.cap-=d; v[tmp.to][tmp.rev].cap+=d; return d; } } } return 0; } int max_flow(int s,int t) { int flow=0; for(;;) { memset(used,false,sizeof(used)); int f=dfs(s,t,INF); if(f==0) return flow; flow+=f; } } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { memset(v,0,sizeof(v)); for(int i=0; i<n; i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add_Node(x,y,z); } for (int i = 1; i <= m; ++i) { cout << " " << i << endl; for (int j = 0; j < v[i].size(); ++j) { cout << v[i][j].rev << endl; } } printf("%d\n",max_flow(1,m)); } } ------------------------------------------------------------------------------------------------------- 3.Dinic /* int Dinic(int s, int t) 返回最大流量 参数:s:开始节点 t:终止节点 数组模拟邻接表 */ //模板区 #include <bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f #define maxn 210 #define Pair pair<int, int> #define mem(a, b) memset(a, b, sizeof(a)) using namespace std; int level[maxn]; int d[maxn]; int prev[maxn]; int cnt; typedef struct node { int v, cap; int next; }edge; edge g[maxn<<1]; void init() { cnt = 0; mem(prev, -1); mem(g, 0); } void read_g(int u, int v, int cap) { g[cnt].v = v; g[cnt].cap = cap; g[cnt].next = prev[u]; prev[u] = cnt++; g[cnt].v = u; g[cnt].cap = 0; //逆向初始为0 g[cnt].next = prev[v]; prev[v] = cnt++; } int bfs(int s, int t) { memset(level, 0, sizeof level); level[s] = 1; queue<int> que; que.push(s); while (!que.empty()) { int x = que.front(); que.pop(); if(x == t) return 1; for (int i = prev[x]; i != -1; i = g[i].next) { int v = g[i].v; int f = g[i].cap; if(!level[v] && f > 0) { level[v] = level[x] + 1; que.push(v); } } } return 0; } int dfs(int u, int t, int cap) { if(u == t) return cap; int ret = 0; for (int i = prev[u]; i != -1; i = g[i].next) { int v = g[i].v; int f = g[i].cap; if(level[u]+1 == level[v] && f > 0) { int mins = min(cap - ret, f); f = dfs(v, t, mins); g[i].cap -= f; g[i^1].cap += f; ret += f; if(ret == cap) return ret; } } return ret; } int Dinic(int s, int t) //Dinic { int ans = 0; while(bfs(s, t)) ans += dfs(s, t, INF); return ans; } //模板区 int main() { int a, b, c, N, M; while (~scanf("%d%d", &N, &M)) { init(); while (N--) { scanf("%d%d%d", &a, &b, &c); read_g(a, b, c); /// 双向建图,逆向初始为0流量 } cout << Dinic(1, M) << endl; } return 0; } --------------------------------------------------------------------------------------------------------- 1.强连通分量tarjan算法 /* int tarjan (int u) 以u为起点 有向图临街表建图 强连通分量解释: 子图中任意两点a,b a可以到达b, b可以到达a */ using namespace std; stack<int> stk; vector<int> vec[maxn]; int low[maxn]; int dfs[maxn]; bool vis[maxn]; int cnt; int n, m; void init() { for (int i = 1 ; i <= n; ++i) { vec[i].clear(); dfs[i] = 0; low[i] = 0; vis[i] = false; } cnt = 0; } int tarjan (int u) //递归处理的点 { dfs[u] = low[u] = ++cnt; //时间线 stk.push(u); //进栈 vis[u] = true; //标记是否在栈中 for (int i = 0; i < vec[u].size(); ++i) { int v = vec[u][i]; if(!dfs[v]) { //如果没被访问过 tarjan(v); low[u] = min(low[u], low[v]); //比较谁是最小根 } else if(vis[v]) { //如果在栈中 low[u] = min(low[u], dfs[v]); //判断最小根 } } if(low[u] == dfs[u]) { //发现是整个强连通分量子树里的最小根 int temp; do{ temp = stk.top(); printf("%d", temp); vis[temp] = false; stk.pop(); }while (u != temp); cout << endl; } return 0; } int main() { init(); cin >> n >> m; for (int i = 1; i <= m; ++i) { int a, b; cin >> a >> b; vec[a].push_back(b); } for (int i = 1; i <= n; ++i) { //扫描所有的点 if(!dfs[i]) tarjan(i); } return 0; } ---------------------------------------------------------------------------------------------------------- 2.连通图的割点 /* void dfs(int now,int father,int depth){ 参数:now:当前节点, father:当前节点的父节点, depth记录dfs标号 采用邻接表存储图,该算法的时间复杂度应与DFS相同,为O(V+E) */ //WHITE:标未访问 //GREY: 标访问到了,在处理中 //BLACK: 标处理完毕。 const int N = 110; const int WHITE = 0,GREY = 1,BLACK = 2; //标记值 int map ; int col ,low ,dep ;//color int n,m; bool tag ;//标记点i是否割点 //求0-1图的割点 void dfs(int now,int father,int depth){ col[now] = GREY; //相当于进栈 dep[now] = depth; int child = 0; for(int i=1;i<=n;i++){ //第i个邻接点 if(map[now][i]==0)continue; if(i != father && col[i] == GREY) low[now] = min(low[now], dep[i]);//low需要被初始化成大数 if(col[i] == WHITE){ // 这是now的子节点 dfs(i, now, depth+1); child = child + 1; //孩子的数量 low[now] = min(low[now], low[i]); //割点: now是跟且有多个孩子 or now不是根且 if((now==1&&child>1)||(now!=1&&low[i]>=dep[now])) //now 不是根 tag[now] = 1;//注意:需要记录该割点增加几个联通分量的操作需要在这里cnt++ } } col[now] = BLACK; } void init(){ mem(map,0); mem(col,0); mem(tag,0); mem(low[i], INF); //low应该被初始化成大值 } int main(){ int a,b; cin>>n>>m; init(); for(int i=0;i<m;i++){ cin>>a>>b; map[a][b] = map[b][a] = 1;//无向图 } dfs(1,1,0);//dfs(root,root,0)的形式调用就行了 int ans=0; for(int i=1;i<=n;i++) if(tag[i])cout<<i<<' '; cout< b110 ;<endl; system("pause"); return 0; } ------------------------------------------------------------------------------ 3.欧拉回路(Fleury算法) /* void fleury(int u) 参数:u 为起点 欧拉回路解释:图G中存在这样一条路径,使得它恰通过G中每条边一次,且该路径是一个圈 */ stack<int> stk; int top; int n, m, s, t; int mp[maxn][maxn]; void dfs(int x) { stk.push(x); for (int i = 1; i <= n; ++i) if(mp[x][i]) { mp[x][i] = mp[i][x] = 0; //删除次边 dfs(i); break; } } void fleury(int u) { int brige; top = 0; stk.push(u); while (!stk.empty()) { brige = 1; int now = stk.top(); for (int i = 1; i <= n; ++i) //搜索一条边不是割边 if (mp[now][i]) { brige = 0; break; } if (brige) { //如果没有点可以扩展, 输出并出栈 printf("%d ", now); stk.pop(); } else { //否则继续搜索欧拉路径 stk.pop(); dfs(now); } } } int main() { int x, y, deg, num; mem(mp, 0); scanf("%d%d", &n, &m); for (int i = 1; i <= m; ++i) { scanf("%d%d", &x, &y); mp[x][y] = mp[y][x] = 1; } s = 1; //开始点 如果所有点度数全为偶数那就从1开始搜 num = 0; //记录度数为奇数的点的个数 for (int i = 1; i <= n; ++i) { deg = 0; for (int j = 1; j <= n; ++j) deg += mp[i][j]; if(deg % 2 == 1) { ++num; //度数为奇数+1 s = i; } } if(num == 0 || num == 2) { //度数为奇数的点数量必须是0或者2 fleury(s); } else printf("no euler path\n"); return 0; } ---------------------------------------------------------------------------------------- 4.哈密顿回路(回溯) /* bool hcs(int s) 返回是否存在哈密顿回路 参数: s:起始点 邻接表 + dfs 时间复杂度 O(n) 哈密顿回路:由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次 1.Dirac定理:设一个无向图中有 N 个节点, 若所有节点的度数都大于等于 N/2, 则汉密尔顿回路一定存在。 */ vector<int> vec[maxn]; int path[maxn]; //记录路径 bool vis[maxn]; //是否被访问过 bool link[maxn]; //记录其余点和起点是否直接相连 //缺点是必须之前输入起点 补救是可以建立邻接矩阵存图 int v, m; void print() // 打印哈密顿回路 { for (int i = 1; i <= v; ++i) { printf("%d ", path[i]); } printf("%d", path[1]); //回路 } bool hc(int u, int c) //u为上一个访问的 和 第c个点 { if (c > v) { //已访问v个顶点 if (link[u] == 1) return true; else return false; } for (int i = 0; i < vec[u].size(); ++i) { int child = vec[u][i]; if (!vis[child]) { vis[child] = true; path[c] = child; if(hc(child, c + 1)) return true; vis[child] = false; } } return false; } bool hcs(int s) { mem(path, -1); mem(vis, false); vis[s] = true; //起点为s path[1] = s; if (!hc(s, 2)) { printf("no hamCycleStart\n"); return false; } print(); return true; } int main() { int start; scanf("%d%d%d", &v, &m, &start); //顶点和边数 mem(link, false); for (int i = 1; i <= v; ++i){ vec[i].clear(); } for (int i = 1 ; i <= m; ++i) { int a, b; scanf("%d%d", &a, &b); if(a == start) link[b] = true; if(b == start) link[a] = true; vec[a].push_back(b); //无向图邻接表建图 vec[b].push_back(a); } hcs(start); return 0; } ------------------------------------------------------------------------------------- 5.判断图的可图性 /* 度序列:若把图 G 所有顶点的度数排成一个序列 S,则称 S 为图 G 的度序列 判定过程:(1)对当前数列排序,使其呈递减, (2)从S【2】开始对其后S【1】个数字-1, (3)一直循环直到当前序列出现负数(即不是可图的情况)或者当前序列全为0 (可图)时退出。 */ int T, n; int in[maxn], x[maxn]; int g[maxn][maxn]; int main() { _; cin >> T; while (T--) { cin >> n; for (int i = 1; i <= n; ++i) { cin >> x[i]; } mem(g, 0); bool flag = true; for (int i = 1; i <= n; ++i) { int v = 0; for (int j = 1; j <= n; ++j) { if(i != j && x[j] && x[i]) { x[j]--; x[i]--; g[i][j] = 1; g[j][i] = 1; } } if(x[i]) { flag = false; break; } } if (flag) { cout << "YES" <<endl; for (int i = 1; i <= n; ++i) { for (int j =1; j <= n; ++j) { if(j - 1) cout << " "; cout << g[i][j]; } cout << endl; } } else cout << "NO" << endl; if(T) cout << endl; } return 0; } ---------------------------------------------------------------------------------------- 待续.........
相关文章推荐
- “玲珑杯”ACM比赛 Round #18 A -- 图论你先敲完模板(DP+思路)
- 图论 --- spfa + 链式向前星 (模板题) dlut 1218 : 奇奇与变形金刚
- 模板整理: 图论---网络流/最小费用最大流
- 图论模板小汇总
- 图论之2-sat模板
- [模板]图论
- 模板-图论
- 模板之图论
- 【笔记+模板】图论 持续更新中
- 图论 有向图的强连通分量(模板)LA 4287
- 图论模板
- 图论--最近公共祖先问题(LCA)模板
- 玲珑杯1146-图论你先敲完模板【dp方程】
- “玲珑杯”ACM比赛 Round #18 C -- 图论你先敲完模板
- POJ 1144 Network 图论 求割点模板
- [NBUT 1642 简单的图论问题?] dijkstra 模板
- 图论--连通图计数模板
- 图论之离线lca模板
- 图论最短路dijkstra----poj1797模板题
- NOIP复赛复习(五)程序对拍与图论模板