您的位置:首页 > 编程语言 > C语言/C++

图论模板

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 图论 c语言 遍历