图论模板

//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[N];
int dis[N], pre[N];

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]=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[N];
bool used[N];

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[N][N];
int col[N],low[N],dep[N];//color
int n,m;
bool tag[N];//标记点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<<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;
}
----------------------------------------------------------------------------------------
待续.........















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TWSF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值