比赛算法模板 持续更新。。。

        先来个大礼包图论500 题

一、最短路

1、链式前向星spfa

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#define inf 0x3f3f3f3f  
  
using namespace std;  
  
const int maxn = 1005;  
  
int n, m, s, t;   //n为点数 s为源点  
int head[maxn]; //head[from]表示以head为出发点的邻接表表头在数组es中的位置,开始时所有元素初始化为-1  
int d[maxn]; //储存到源节点的距离,在Spfa()中初始化  
int cnt[maxn];  
bool inq[maxn]; //这里inq作inqueue解释会更好,出于习惯使用了inq来命名,在Spfa()中初始化  
int nodep;  //在邻接表和指向表头的head数组中定位用的记录指针,开始时初始化为0  
int pre[maxn];  
  
struct node {  
    int v, w, next;  
}es[maxn << 2];  
  
void init() {  
    for(int i = 1; i <= n; i++) {  
        d[i] = inf;  
        inq[i] = false;  
        cnt[i] = 0;  
        head[i] = -1;  
        pre[i] = -1;  
    }  
    nodep = 0;  
}  
  
void addedge(int from, int to, int weight)  
{  
    es[nodep].v = to;  
    es[nodep].w = weight;  
    es[nodep].next = head[from];  
    head[from] = nodep++;  
}  
  
bool spfa()  
{  
    queue<int> que;  
    d[s] = 0;    //s为源点  
    inq[s] = 1;  
    que.push(s);  
    while(!que.empty()) {  
        int u = que.front();  
        que.pop();  
        inq[u] = false;   //从queue中退出  
        //遍历邻接表  
        for(int i = head[u]; i != -1; i = es[i].next) {  //在es中,相同from出发指向的顶点为从head[from]开始的一项,逐项使用next寻找下去,直到找到第一个被输  
                                                        //入的项,其next值为-1  
            int v = es[i].v;  
            if(d[v] > d[u] + es[i].w) { //松弛(RELAX)操作  
                d[v] = d[u] + es[i].w;  
                pre[v] = u;  
                if(!inq[v]) {      //若被搜索到的节点不在队列que中,则把to加入到队列中去  
                    inq[v] = true;  
                    que.push(v);  
                    if(++cnt[v] > n) {  
                        return false;  
                    }  
                }  
            }  
        }  
    }  
    return true;  
}  
  
void putpath() {  
    stack<int> path;  
    int now = t;  
    while(1) {  
        path.push(now);  
        if(now == s) {  
            break;  
        }  
        now = pre[now];  
    }  
    while(!path.empty()) {  
        now = path.top();  
        path.pop();  
        printf("%d\n", now);  
    }  
}  
  
int main()  
{  
    while(cin >> m >> n) {  
        init();  
        int a, b, c;  
        while(m--) {  
            cin >> a >> b >> c;  
            addedge(a, b, c);  
            addedge(b, a, c);  
        }  
        s = n, t = 1;  
        if(spfa()) {  
            cout << d[t] << endl;  
        }//putpath();  
    }  
    return 0;  
}  

2、链式前向星dijkstra

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#define inf 0x3f3f3f3f  
  
using namespace std;  
  
const int maxn = 1005;  
const int maxm = 100500;  
  
int n = maxn, m, s, t;   //n为点数 s为源点  
int head[maxn]; //head[from]表示以head为出发点的邻接表表头在数组es中的位置,开始时所有元素初始化为-1  
int d[maxn]; //储存到源节点的距离,在init()中初始化  
bool vis[maxn]; //是否访问过  
int nodep;  //在邻接表和指向表头的head数组中定位用的记录指针,开始时初始化为0  
int pre[maxn];  
  
  
struct node {  
    int num;  
    int dis;  
    node (int a = 0, int b = 0) : num(a), dis(b) {}  
    friend bool operator <(node a, node b) {  
        return a.dis > b.dis;  
    }  
};  
  
struct edge {  
    int v, next;  
    int w;  
}es[maxm];  
  
void init() {  
    for(int i = 0; i < maxn; i++) {  
        d[i] = inf;  
        vis[i] = false;  
        head[i] = -1;  
        pre[i] = -1;  
    }  
    nodep = 0;  
}  
  
void addedge(int from, int to, int weight)  
{  
    es[nodep].v = to;  
    es[nodep].w = weight;  
    es[nodep].next = head[from];  
    head[from] = nodep++;  
}  
  
void dijkstra()  
{  
    priority_queue<node> pq;  
    d[s] = 0;    //s为源点  
    pq.push(node(s, 0));  
    while(!pq.empty()) {  
        node num = pq.top();  
        pq.pop();  
        int u = num.num;  
        if(vis[u]) continue;  
        vis[u] = 1;  
        //遍历邻接表  
        for(int i = head[u]; i != -1; i = es[i].next) {  //在es中,相同from出发指向的顶点为从head[from]开始的一项,逐项使用next寻找下去,直到找到第一个被输  
                                                        //入的项,其next值为-1  
            int v = es[i].v;  
            if(!vis[v] && d[v] > d[u] + es[i].w) { //松弛(RELAX)操作  
                d[v] = d[u] + es[i].w;  
                pre[v] = u;  
                pq.push(node(v, d[v]));  
            }  
        }  
    }  
}  
  
void putpath() {  
    stack<int> path;  
    int now = t;  
    while(1) {  
        path.push(now);  
        if(now == s) {  
            break;  
        }  
        now = pre[now];  
    }  
    while(!path.empty()) {  
        now = path.top();  
        path.pop();  
        printf("%d\n", now);  
    }  
}  
  
int main()  
{  
    while(cin >> m >> n) {  
        init();  
        int a, b, c;  
        while(m--) {  
            cin >> a >> b >> c;  
            addedge(a, b, c);  
            addedge(b, a, c);  
        }  
        s = n, t = 1;  
        dijkstra();  
        cout << d[t] << endl;  
        //putpath();  
    }  
    return 0;  
}  

3、最短路判关键路径:d1[x[i]] + d[y[i]] + c[i] == d1[b]

                                        d[i] + cost[i][j] == d[j] 

4、邻接矩阵spfa

int cost[MAX_V][MAX_V];  
int d[MAX_V];  
bool used[MAX_V];  
int p[MAX_V];  
int in[MAX_V];  
  
void init() {  
    for(int i = 0; i <= N; i++) {  
        cost[i][i] = 0;  
        for(int j = 0; j < i; j++) {  
            cost[i][j] = cost[j][i] = INF;  
        }  
        used[i] = false;  
        d[i] = INF;  
        p[i] = -1;  
        in[i] = 0;  
    }  
}  
  
bool spfa(int s) {  
    queue<int > que;  
    que.push(s);  
    used[s] = true;  
    d[s] = 0;  
    in[s]++;  
    while(!que.empty()) {  
        int u = que.front();  
        que.pop();  
        used[u] = false;  
        for(int v = 1; v <= N; v++) {  
            if(d[v] > d[u] + cost[u][v]) {  
                d[v] = d[u] + cost[u][v];  
                if(!used[v]) {  
                    que.push(v);  
                    used[v] = true;  
                    in[v]++;  
                    if(in[v] >= N)  
                        return false;  
                }  
            }  
        }  
    }  
    return true;  
}  

5、邻接矩阵dijkstra

int cost[MAX_V][MAX_V];  
int d[MAX_V];  
bool used[MAX_V];  
int N, M, C, A, B;  
  
void init() {  
    for(int i = 0; i <= N; i++) {  
        for(int j = 0; j <= i; j++) {  
            if(i == j) {  
                cost[i][j] = 0;  
            }  
            else {  
                cost[i][j] = cost[j][i] = INF;  
            }  
        }  
        used[i] = false;  
        d[i] = INF;  
    }  
}  
  
void dijkstra(int s) {  
    for(int i = 1; i <= N; i++) {  
        d[i] = cost[s][i];  
    }  
    used[s] = true;  
    for(int i = 2; i <= N; i++) {  
        int mind = INF;  
        int u;  
        for(int j = 1; j <= N; j++) {  
            if(!used[j]) {  
                if(d[j] < mind) {  
                    mind = d[j];  
                    u = j;  
                }  
            }  
        }  
        if(mind == INF) break;  
        used[u] = true;  
        for(int j = 1; j <= N; j++) {  
            if(!used[j]) {  
                if(d[j] > d[u] + cost[u][j]) {  
                    d[j] = d[u] + cost[u][j];  
                }  
            }  
        }  
    }  
}  

6、floyd算法

void Floyd(int n)/*贪心迭代*/  
{  
    int i,j,k;  
    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];  
                }  
} 

7、差分约束思想

差分约束系统的解法如下:

1、  根据条件把题意通过变量组表达出来得到不等式组,注意要发掘出隐含的不等式,比如说前后两个变量之间隐含的不等式关系。

2、  进行建图:

首先根据题目的要求进行不等式组的标准化。

(1)、如果要求取最小值,那么求出最长路,那么将不等式全部化成xi – xj >= k的形式,这样建立j->i的边,权值为k的边,如果不等式组中有xi – xj > k,因为一般题目都是对整形变量的约束,化为xi – xj >= k+1即可,如果xi – xj = k呢,那么可以变为如下两个:xi – xj >= k, xi – xj <= k,进一步变为xj – xi >= -k,建立两条边即可。

(2)、如果求取的是最大值,那么求取最短路,将不等式全部化成xi – xj <= k的形式, 这样建立j->i的边,权值为k的边,如果像上面的两种情况,那么同样地标准化就行了。

(3)、如果要判断差分约束系统是否存在解,一般都是判断环,选择求最短路或者最长路求解都行,只是不等式标准化时候不同,判环地话,用spfa即可,n个点中如果同一个点入队超过n次,那么即存在环。

值得注意的一点是:建立的图可能不联通,我们只需要加入一个超级源点,比如说求取最长路时图不联通的话,我们只需要加入一个点S,对其他的每个点建立一条权值为0的边图就联通了,然后从S点开始进行spfa判环。最短路类似。

3、  建好图之后直接spfa或bellman-ford求解,不能用dijstra算法,因为一般存在负边,注意初始化的问题。

二、最小生成树 

1、kruskal

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <algorithm>  
#include <cmath>  
#include <cstdlib>  
#include <limits>  
#include <vector>  
#include <sstream>  
#define ll long long  
  
using namespace std;  
  
const int maxn = 10000 + 10;  
const int maxm = 1000000 + 10;  
const int inf = 0x3f3f3f3f;  
  
int fa[maxn];  
int rnk[maxn];  
  
int n, m;  
  
struct node {  
    int a, b;  
    int c;  
    friend bool operator < (node x, node y) {  
        return x.c < y.c;  
    }  
}q[maxn];  
  
void init() {  
    for(int i = 0; i <= n; i++) {  
        fa[i] = i;  
        rnk[i] = 0;  
    }  
}  
  
int getf(int x) {  
    if(x != fa[x]) {  
        fa[x] = getf(fa[x]);  
    }  
    return fa[x];  
}  
  
void unions(int x, int y) {  
    x = getf(x);  
    y = getf(y);  
    if(x == y) return;  
    if(rnk[x] < rnk[y]) {  
        fa[x] = y;  
    }  
    else {  
        fa[y] = x;  
        if(rnk[x] == rnk[y]) rnk[x]++;  
    }  
}  
  
bool same(int x, int y) {  
    return getf(x) == getf(y);  
}  
  
int kruskal(int n, int m) {  
    init();  
    sort(q + 1, q + 1 + m);  
    int ans = 0;  
    int nedge = 0;  
    for(int i = 1; i <= m && nedge != n - 1; i++) {  
        if(getf(q[i].a) != getf(q[i].b)) {  
            unions(q[i].a, q[i].b);  
            ans += q[i].c;  
            nedge++;  
        }  
    }  
    if(nedge < n - 1) ans = -1;  
    return ans;  
}  
  
int main()  
{  
    int T, kcase = 0;  
    while(scanf("%d", &n) && n) {  
        scanf("%d", &m);  
        for(int i = 1; i <= m; i++) {  
           scanf("%d %d %d", &q[i].a, &q[i].b, &q[i].c);  
        }  
        int ans = kruskal(n, m);  
        printf("%d\n", ans);  
    }  
    return 0;  
}  

2、kruskal优先队列形式

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#define MAX_V 100005  
#define INF 0x3f3f3f3f  
  
using namespace std;  
  
const int inf = 0x3f3f3f3f;  
const int maxn = 105;  
  
int V;  
int f[maxn];  
int x[maxn], y[maxn];  
  
struct edge {  
    int u, v;  
    int cost;  
    friend bool operator < (edge e1, edge e2) {  
        return e1.cost > e2.cost;  
    }  
};  
  
priority_queue<edge> pq;  
  
void init(int x)  
{  
    for(int i = 0; i <= x; i++) {  
        f[i] = i;  
    }  
}  
  
int getf(int x)  
{  
    if(x != f[x])  
        f[x] = getf(f[x]);  
    return f[x];  
}  
  
void unions(int x, int y)  
{  
    x = getf(x);  
    y = getf(y);  
    if(x != y)  
        f[y] = x;  
}  
  
bool same(int x, int y) {  
    return getf(x) == getf(y);  
}  
  
int kruskal() {  
    int res = 0;  
    while(!pq.empty()) {  
        edge e = pq.top();  
        pq.pop();  
        if(!same(e.u, e.v)) {  
            unions(e.u, e.v);  
            res += e.cost;  
        }  
    }  
    return res;  
}  
  
int main()  
{  
    int n, m;  
       while( cin >> n && n) {  
        int a, b, c, k;  
        edge e;  
        init(maxn);  
        for(int i = 0; i < n * (n - 1) / 2; i++) {  
            scanf("%d %d %d %d", &a, &b, &c, &k);  
            if(k) {  
                unions(a, b);  
            }  
            else {  
                e.u = a, e.v = b, e.cost = c;  
                pq.push(e);  
            }  
        }  
  
        int ans = kruskal();  
        cout << ans << endl;  
        while(!pq.empty()) {  
            pq.pop();  
        }  
    }  
    return 0;  
}  

3、prim 堆优化

#include<stdio.h>    
#include<string.h>    
#include<algorithm>    
#include<queue>    
#include<iostream>    
using namespace std;    
    
int n;    
int head[3010];    
    
struct node{    
    int to;    
    int c;    
    int next;    
}edge[200000];    
    
struct ver{    
    int x;    
    int dis;    
    bool operator < (const ver& t) const{    
        return dis>t.dis;    
    }    
};    
    
priority_queue<ver> q;    
    
int  prime()    
{    
    int res = 0;    
    //init a collection    
    int dis[3010];    
    int v_j;    
    int vis[3010];    
    for(int i = 0; i<n; i++) {    
        dis[i] = 0x3f3f3f;    
    }    
   // q.push(0);    
    vis[0] = 1;    
    for(int i = head[0]; i!= -1; i = edge[i].next) {    
        v_j = edge[i].to;    
        dis[v_j] = edge[i].c;    
        q.push(ver{v_j,dis[v_j]});    
    }    
    dis[0] = 0x3f3f3f;    
    for(int i = 1; i<n; i++) {    
        int temp_j = 0;    
        int min_c = 0x3f3f3f;    
        //找出dis中的最小值的坐标,这里体现log n    
        ver t_ver = q.top();    
        q.pop();    
        temp_j = t_ver.x;    
        res += t_ver.dis;    
        vis[temp_j] = 1;    
        for(int j = head[temp_j]; j!=-1; j = edge[j].next) {    
            v_j = edge[j].to;    
            if(!vis[v_j] && dis[v_j] > edge[j].c) {    
                dis[v_j] = edge[j].c;    
                q.push(ver{v_j,dis[v_j]});    
            }    
        }    
    }    
    return res;    
}    
    
init() {    
    for(int i = 0;i<n;i++) {    
        head[i] = -1 ;    
    }    
}    
    
int main()    
{    
    int m;    
    while(~scanf("%d%d",&n,&m)) {    
        init();    
        int u,v,c;    
        int k = 0;    
        for(int i = 0;i<m;i++) {    
            scanf("%d%d%d",&u,&v,&c);    
            edge[k].next = head[u];    
            edge[k].to = v;    
            edge[k].c = c;    
            head[u] = k;    
            k++;    
            edge[k].next = head[v];    
            edge[k].to = u;    
            edge[k].c = c;    
            head[v] = k;    
            k++;    
        }    
        cout<<prime()<<endl;    
    }    
}   

4、次小生成树

#include <iostream>  
#include <algorithm>  
#include <queue>  
#include <cstring>  
#include <cstdio>  
using namespace std;  
  
const int inf = 0x3f3f3f3f;  
const int maxn = 10001;  
int n, m;  
int num;  
int p[maxn];  
int maxx[101][101];  
  
struct Edge     //原始图  
{  
    int from;  
    int to;  
    int w;  
    bool flag;  
    friend bool operator < (Edge x, Edge y) {  
        return x.w < y.w;  
    }  
}e[maxn];  
  
struct Tree     //最小生成树  
{  
    int to;  
    int w;  
    int next;  
}tree[202];  
int index[101];  
  
struct Node     //生成树的结点  
{  
    int seq;    //结点编号  
    int maxx;    //从某个点到它的路径中的最大边的长度  
};  
  
void makeSet()  
{  
    for(int i = 0; i <= n; i++)  
    {  
        p[i] = i;  
    }  
}  
  
int findSet(int x)  
{  
    if(x != p[x])  
        p[x] = findSet(p[x]);  
    return p[x];  
}  
  
void addEdge(int from, int to, int w)  
{  
    tree[num].to = to;  
    tree[num].w = w;  
    tree[num].next = index[from];  
    index[from] = num++;  
}  
  
int kruscal()  
{  
    int x, y;  
    int edgeNum = 0;  
    int result = 0;  
    makeSet();  
    sort(e, e + m);  
    for(int i = 0; i < m; i++) {  
        x = findSet(e[i].from);  
        y = findSet(e[i].to);  
        if(x != y) {  
            edgeNum++;  
            addEdge(e[i].from,e[i].to,e[i].w);  
            addEdge(e[i].to,e[i].from,e[i].w);  
            e[i].flag = true;  
            p[x] = y;  
            result += e[i].w;  
        }  
    }  
    return edgeNum == n - 1 ? result : -1;  
}  
  
void bfs(int p)  
{  
    bool used[101];  
    memset(used, 0, sizeof(used));  
    queue<Node> que;  
    Node now, adj;  
    now.maxx = 0;  
    now.seq = p;  
    que.push(now);  
    used[p] = true;  
    while(!que.empty()) {  
        Node q = que.front();  
        que.pop();  
        for(int i = index[q.seq]; i != -1; i = tree[i].next) {  
            adj.seq = tree[i].to;  
            adj.maxx = tree[i].w;  
            if(!used[adj.seq]) {  
                if(q.maxx > adj.maxx)  
                    adj.maxx = q.maxx;  
                maxx[p][adj.seq] = adj.maxx;  
                used[adj.seq] = true;  
                que.push(adj);  
            }  
        }  
    }  
}  
  
void second_MST()  
{  
    int mst = kruscal();  
    for(int i = 1; i <= n; i++)  
        bfs(i);  
    int smst = inf;  
    for(int i = 0; i < m; i++) {  
        if(!e[i].flag) {  
            if(mst + e[i].w - maxx[e[i].from][e[i].to] < smst)  
                smst = mst + e[i].w - maxx[e[i].from][e[i].to];  
        }  
    }  
    if(smst == mst)  
        printf("Not Unique!\n");  
    else  
        printf("%d\n", mst);  
}  
  
int main()  
{  
    int cases;  
    int a, b, w;  
    scanf("%d", &cases);  
    while(cases--) {  
        scanf("%d %d", &n, &m);  
        for(int i = 0; i < m; i++) {  
            scanf("%d %d %d", &e[i].from, &e[i].to, &e[i].w);  
            e[i].flag = false;  
        }  
        num = 0;  
        memset(index, -1, sizeof(index));  
        second_MST();  
    }  
    return 0;  
}  

三、二分图

1、二分图最大匹配  匈牙利算法

 3个重要结论:

最大匹配数:最大匹配的匹配边的数目

最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择

最大独立集:选取最多的点,使任意所选两点均不相连

最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

最小点覆盖数=最大匹配数

最小路径覆盖 =顶点数-最大匹配数

二分图最大独立集 = 顶点数 - 最大匹配数

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#define INF 0x3f3f3f3f  
  
using namespace std;  
  
const int inf = 0x3f3f3f3f;  
const int maxn = 2555;  
  
int used[maxn];  
int link[maxn];  
int mat[maxn][maxn];  
int gn, gm;  
  
int dfs(int t) {  
    for(int i = 1; i <= gm; i++) {  
        if(!used[i] && mat[t][i]) {  
            used[i] = 1;  
            if(link[i] == -1 || dfs(link[i])) {  
                link[i] = t;  
                return 1;  
            }  
        }  
    }  
    return 0;  
}  
  
int maxmatch() {  
    int num = 0;  
    memset(link, 0xff, sizeof(link));  
    for(int i = 1; i <= gn; i++) {  
        memset(used, 0, sizeof(used));  
        if(dfs(i)) {  
            num++;  
        }  
    }  
    return num;  
}  
  
int main()  
{  
    int n, m, k;  
    while(~scanf("%d", &n) && n){  
        scanf("%d %d", &m, &k);  
        memset(mat, 0, sizeof(mat));  
        gn = n;  
        gm = m;  
        int a, x, y;  
        for(int i = 0; i < k; i++) {  
            scanf("%d %d %d", &a, &x, &y);  
            mat[x][y] = 1;  
        }  
        int res = maxmatch();  
        printf("%d\n", res);  
    }  
    return 0;  
}  

2、二分图最优匹配 KM算法 (最大权匹配)

尽量使用原有匹配方法:由于最优匹配最终必有N条边, 所以将原图所有的边的权值乘以(N+1)扩大(N + 1)倍,并且如果边是原匹配中的一条边就再 加 1     所以边权就变为了 (N+1)的倍数或(N+1)的倍数 + 1      

所以最终得到的最优权值     除以(N+1)就是最优权值解   、 %(N+1)就是复用旧边的条数

老边加1保证了在最优解唯一时不丢失最优解,在最优解不唯一时保证了优先用原匹配边

这样就实现尽量使用原匹配边的情况下求最优匹配

最小权匹配:只需把权值取反,变为负的,再用KM算出最大权匹配,取反则为其最小权匹配。

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#define INF 0x3f3f3f3f  
  
using namespace std;  
  
const int inf = 0x3f3f3f3f;  
const int maxn = 1005;  
  
int n, nx, ny, w[maxn][maxn];  
int link[maxn], lx[maxn], ly[maxn], slack[maxn];  
bool visx[maxn], visy[maxn];  
  
  
bool dfs(int x){  
    visx[x] = true;  
    for(int y = 1; y <= ny; y++){  
        if(visy[y]) continue;  
        int t = lx[x] + ly[y] - w[x][y];  
        if(t == 0){  
            visy[y] = true;  
            if(link[y] == -1 || dfs(link[y])){  
                link[y] = x;  
                return true;  
            }  
        }  
        else  
            if(slack[y] > t){ //不在相等子图中slack取最小的  
                slack[y] = t;  
            }  
    }  
    return false;  
}  
  
int KM(){  
    nx = ny = n;  
    memset(link, -1, sizeof(link));  
    memset(ly, 0, sizeof(ly));  
    for(int i = 1; i <= nx; i++){ //lx 初始化为与它关联边中最大的  
        lx[i] = -INF;  
        for(int j = 1; j <= ny; j++){  
            if(w[i][j] > lx[i]) lx[i] = w[i][j];  
        }  
    }  
  
    for(int x = 1; x <= nx; x++){  
        for(int i = 1; i <= ny; i++){  
            slack[i] = INF;  
        }  
        while(true){  
            memset(visx, false, sizeof(visx));  
            memset(visy, false, sizeof(visy));  
  
            //若成功(找到了增广路),则该点增广完毕,下一个点进入增广  
            if(dfs(x)) break;  
  
            //若失败,则需要改变一些点的顶标,使得图中可行边的数量增加  
            //(1)将所有在增广轨中的 X 方点的标号 全部减去一个常数 d ;  
            //(2)将所有在增广轨中的 Y 方点的标号 全部加上一个常数 d ;  
            int d = INF;  
            for(int i = 1; i <= ny; i++){  
                if(!visy[i] && d > slack[i]) d = slack[i];  
            }  
            for(int i = 1; i <= nx; i++){  
                if(visx[i]) lx[i] -= d;  
            }  
            for(int i = 1; i <= ny; i++){  
                if(visy[i]) ly[i] += d;  
                else slack[i] -= d;  
            }  
        }  
    }  
    int res = 0;  
    for(int i = 1; i <= ny; i++){  
        if(link[i] > -1) res += w[link[i]][i];  
    }  
    return res;  
}  
  
int main ()  
{  
    while(~scanf("%d", &n)) {  
        for(int i = 1; i <= n; i++) {  
            for(int j = 1; j <= n; j++) {  
                scanf("%d", &w[i][j]);  
            }  
        }  
        int ans = KM();  
        printf("%d\n", ans);  
    }  
    return 0;  
}  

四、网络流

1、最大流EK算法

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#define INF 0x3f3f3f3f  
  
using namespace std;  
  
const int inf = 0x3f3f3f3f;  
const int maxn = 405;  
  
struct Edge {  
    int v, nxt, w;  
};  
  
struct Node {  
    int v, id;  
};  
  
int n, m, ecnt;  
bool vis[maxn];  
int head[maxn];  
Node pre[maxn];  
Edge edge[maxn];  
  
void init() {  
    ecnt = 0;  
    memset(edge, 0, sizeof(edge));  
    memset(head, -1, sizeof(head));  
}  
  
void addEdge(int u, int v, int w) {  
    edge[ecnt].v = v;  
    edge[ecnt].w = w;  
    edge[ecnt].nxt = head[u];  
    head[u] = ecnt++;  
}  
  
bool bfs(int s, int t) {  
    queue <int> que;  
    memset(vis, 0, sizeof(vis));  
    memset(pre, -1, sizeof(pre));  
    pre[s].v = s;  
    vis[s] = true;  
    que.push(s);  
    while(!que.empty()) {  
        int u = que.front();  
        que.pop();  
        for(int i = head[u]; i + 1; i = edge[i].nxt) {  
            int v = edge[i].v;  
            if(!vis[v] && edge[i].w) {  
                pre[v].v = u;  
                pre[v].id = i;  
                vis[v] = true;  
                if(v == t) return true;  
                que.push(v);  
            }  
        }  
    }  
    return false;  
}  
  
int EK(int s, int t) {  
    int ans = 0;  
    while(bfs(s, t)) {  
        int mi = INF;  
        for(int i = t; i != s; i = pre[i].v) {  
            mi = min(mi, edge[pre[i].id].w);  
        }  
        for(int i = t; i != s; i = pre[i].v) {  
            edge[pre[i].id].w -= mi;  
            edge[pre[i].id ^ 1].w += mi;  
        }  
        ans += mi;  
    }  
    return ans;  
}  
  
int main ()  
{  
    int t, kcase = 0;  
    int N, M, E, k;  
    char ch;  
    while(~scanf("%d %d", &N, &M)) {  
        init();  
        int a, b, c;  
        while(N--) {  
            scanf("%d %d %d", &a, &b, &c);  
            addEdge(a, b, c);  
            addEdge(b, a, 0);  
        };  
        int ans = EK(1, M);  
        printf("%d\n", ans);  
    }  
    return 0;  
}  

2、最大流dinic当前弧优化

const int inf = 0x3f3f3f3f;  
const int maxn = 2005;  
  
int n, m;//点数、边数  
int sp, tp;//原点、汇点  
  
struct node  {  
    int v, next;  
    int cap;  
}mp[maxn];  
  
int pre[MAXN], dis[MAXN], cur[MAXN];//cur为当前弧优化,dis存储分层图中每个点的层数(即到原点的最短距离),pre建邻接表  
int cnt = 0;  
  
void init() {  //不要忘记初始化  
    cnt = 0;  
    memset(pre, -1, sizeof(pre));  
}  
  
void add(int u, int v, int w) { //加边  
    mp[cnt].v = v;  
    mp[cnt].cap = w;  
    mp[cnt].next = pre[u];  
    pre[u] = cnt++;  
    mp[cnt].v = u;  
    mp[cnt].cap = 0;  
    mp[cnt].next = pre[v];  
    pre[v] = cnt++;  
}  
  
bool bfs() {  //建分层图  
    memset(dis, -1, sizeof(dis));  
    queue<int>q;  
    while(!q.empty())  
        q.pop();  
    q.push(sp);  
    dis[sp] = 0;  
    int u, v;  
    while(!q.empty()) {  
        u = q.front();  
        q.pop();  
        for(int i = pre[u]; i != -1; i = mp[i].next) {  
            v = mp[i].v;  
            if(dis[v] == -1 && mp[i].cap>0) {  
                dis[v] = dis[u] + 1;  
                q.push(v);  
                if(v == tp)  
                    break;  
            }  
        }  
    }  
    return dis[tp] != -1;  
}  
  
int dfs(int u,int cap) {//寻找增广路  
    if(u == tp || cap == 0)  
    return cap;  
    int res = 0, f;  
    for(int i = cur[u]; i != -1; i = mp[i].next) {//  
        int v = mp[i].v;  
        if(dis[v] == dis[u] + 1 && (f = dfs(v, min(cap - res, mp[i].cap))) > 0) {  
            mp[i].cap -= f;  
            mp[i ^ 1].cap += f;  
            res += f;  
            if(res == cap)  
                return cap;  
        }  
    }  
    if(!res)  
        dis[u] = -1;  
    return res;  
}  
  
int dinic() {  
    int ans = 0;  
    while(bfs()) {  
        for(int i = sp; i <= tp; i++)  
            cur[i] = pre[i];  
        ans += dfs(sp, inf);  
    }  
    return ans;  
}  

3、最大流路径输出

        for(int i = 1; i <= m; i++) {  
            for(int p = pre[i + m]; p; p = mp[p].next) {  
                if(mp[p].v != i && mp[p].v != sp && mp[p].v != tp && mp[p].cap != inf) {  
                    sum++;  
                }  
            }  
        }  
        printf("%d %d\n", ans, sum);  
        for(int i = 1; i <= m; i++) {  
            for(int p = pre[i + m]; p; p = mp[p].next) {  
                if(mp[p].v != i && mp[p].v != sp && mp[p].v != tp && mp[p].cap != inf) {  
                    printf("%d %d %d\n", i, mp[p].v, inf - mp[p].cap);  
                }  
            }  
        }  

4、最小费用最大流

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#include <deque>  
#include <sstream>  
#define MAX_V 1005  
#define INF 0x3f3f3f3f  
using namespace std;  
  
//最小费用最大流模版.求最大费用最大流建图时把费用取负即可。  
//无向边转换成有向边时需要拆分成两条有向边。即两次加边。  
const int maxn = 1010;  
const int maxm = 1000200;  
const int inf = 0x3f3f3f3f;  
  
struct Edge {  
    int v, cap, cost, next;  
}p[maxm << 1];  
  
int e, sumFlow, n, m, st, en;  
int head[maxn], dis[maxn], pre[maxn];  
bool vis[maxn];  
void init() {  
    e = 0;  
    memset(head, -1, sizeof(head));  
}  
  
void addEdge(int u, int v, int cap, int cost) {  
    p[e].v = v; p[e].cap = cap; p[e].cost = cost;  
    p[e].next = head[u]; head[u] = e++;  
    p[e].v = u; p[e].cap = 0; p[e].cost = -cost;  
    p[e].next = head[v]; head[v] = e++;  
}  
  
bool spfa(int s,int t, int n) {  
    int u, v;  
    queue<int>q;  
    memset(vis, false, sizeof(vis));  
    memset(pre, -1, sizeof(pre));  
    for(int i = 0; i <= n; i++)  
        dis[i] = inf;  
    vis[s] = true;  
    dis[s] = 0;  
    q.push(s);  
    while(!q.empty()) {  
        u = q.front();  
        q.pop();  
        vis[u] = false;  
        for(int i = head[u]; i != -1; i = p[i].next) {  
            v = p[i].v;  
            if(p[i].cap && dis[v] > dis[u] + p[i].cost) {  
                dis[v] = dis[u] + p[i].cost;  
                pre[v] = i;  
                if(!vis[v]) {  
                    q.push(v);  
                    vis[v] = true;  
                }  
            }  
        }  
     }  
     if(dis[t] == inf)  
         return false;  
     return true;  
}  
  
int MCMF(int s, int t, int n) {  
    int flow = 0; // 总流量  
    int minflow, mincost;  
    mincost = 0;  
    while(spfa(s, t, n)) {  
        minflow = inf + 1;  
        for(int i = pre[t]; i != -1; i = pre[p[i^1].v]) {  
            if(p[i].cap < minflow) {  
                minflow = p[i].cap;  
            }  
        }  
        flow += minflow;  
        for(int i = pre[t]; i != -1; i = pre[p[i^1].v]) {  
            p[i].cap -= minflow;  
            p[i^1].cap += minflow;  
        }  
        mincost += dis[t] * minflow;  
    }  
    sumFlow = flow; // 最大流  
    return mincost;  
}  
  
int x1[105], x2[105], yy[105], y2[105];  
  
int main ()  
{  
    int t, kcase = 0;  
    int N, M;  
    while(~scanf("%d %d", &N, &M) && N && M) {  
        int k1 = 0, k2 = 0;  
        char str[105];  
        for(int i = 0; i < N; i++) {  
            scanf("%s", str);  
            for(int j = 0; j < M; j++) {  
                if(str[j] == 'm') {  
                    x1[++k1] = i;  
                    yy[k1] = j;  
                }  
                if(str[j] == 'H') {  
                    x2[++k2] = i;  
                    y2[k2] = j;  
                }  
            }  
        }  
        init();  
        n = k1;  
        //cout << k1;  
        for(int i = 1; i <= n; i++) {  
            addEdge(0, i, 1, 0);  
            addEdge(n + i, n * 2 + 1, 1, 0);  
            for(int j = 1; j <= n; j++) {  
                int k = (abs(x1[i] - x2[j]) + abs(yy[i] - y2[j]));  
                //cout << " " << w[i][j] ;  
                addEdge(i, j + n, 1, k);  
            }  
            //cout <<endl;  
        }  
        int ans = MCMF(0, 2 * n + 1, 2 * n + 1);  
        printf("%d\n", ans);  
    }  
    return 0;  
}  

5、图的加边去重

void add(int u, int v, int w, int c){  
    int i;  
    for(i = head[u]; i != -1; i = edge[i].next){  
        node E = edge[i];  
        if(v == E.v)  
            break;  
    }  
    if(i != -1){//w 或 c具体看  
        if(edge[i].cost > c)  
            edge[i].cost = c, edge[i ^ 1].cost = -c;  
        return ;  
    }  
    //加上图的正常加边  
}  

五、并查集

1、基础并查集

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<queue>  
#include<cstring>  
#include<string>  
#include <cmath>  
using namespace std;  
  
const int maxn = 1050 + 10;  
const int inf = 1e9 + 7;  
const int N = 1005;// 初始化并查集  
  
int n;  
int father[N];  
  
void init() {  
    for(int i = 0; i < N; i++)  
      father[i] = i;  
}  
int getfather(int x) {  
    while(x != father[x])  
      x = father[x];  
    return x;  
}  
// 合并两个元素所在的集合  
void unions(int x, int y) {  
    x = getfather(x);  
    y = getfather(y);  
    if(x != y)  
       father[x] = y;  
}  
// 判断两个元素是否属于同一个集合  
bool same(int x, int y) {  
    return getfather(x) == getfather(y);  
}  
  
int x[maxn], y[maxn];  
int r[maxn];  
int d, cnt = 0;  
  
bool dic(int a, int b) {  
    if((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]) <= d * d)  
        return 1;  
    return 0;  
}  
  
int main()  
{  
    int T, kcase = 0;  
    cin >> T;  
    while(T--) {  
        int m;  
        cin >> n >> m;  
        init();  
        while(m--) {  
            int a, b;  
            cin >> a >> b;  
            unions(a, b);  
        }  
        m++;  
        for(int i = 1; i <= n; i++) {  
            if(father[i] == i) {  
                m++;  
            }  
        }  
        cout << m << endl;  
    }  
    return 0;  
}  

路径压缩

int getfather(int x) {  
    if(x != father[x])  
      father[x] = getfather(father[x]); // 路径压缩修改的是father数组  
    return father[x];  
}  

秩优化

void unite (int x, int y) {  
    x = getfather(x);  
    y = getfather(y);  
    if(x == y) return;  
    if(rnk[x] < rnk[y]) {  
        father[x] = y;  
    }  
    else {  
        father[y] = x;  
        if(rnk[x] == rnk[y]) rnk[x]++;  
    }  
}

2、带权并查集  向量思想

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<queue>  
#include<cstring>  
#include<string>  
#include <cmath>  
using namespace std;  
  
const int maxn = 200000 + 10;  
const int inf = 1e9 + 7;  
  
int n;  
  
int father[maxn];  
int sum[maxn];  
  
void init(int n) {  
    for(int i = 0; i <= n; i++) {  
      father[i] = i;  
      sum[i] = 0;  
    }  
}  
// 获取根结点  
int getf(int x) {  
    if(father[x] != x) {  
        int t = father[x];  
        father[x] = getf(father[x]);  
        sum[x] += sum[t];  
    }  
    return father[x];  
}  
  
int main()  
{  
    int T, kcase = 0;  
    int m;  
    while(~scanf("%d %d", &n, &m)) {  
        init(n);  
        int ans = 0;  
        int a, b, c;  
        while(m--) {  
            scanf("%d %d %d", &a, &b, &c);  
            a--;  
            int roota = getf(a);  
            int rootb = getf(b);  
            if(roota == rootb) {  
                if(sum[a] - sum[b] != c) {  
                    ans++;  
                }  
            }  
            else {  
                father[roota] = rootb;  
                sum[roota] = sum[b] - sum[a] + c;  
            }  
        }  
        cout << ans << endl;  
    }  
    return 0;  
}  

3、种类并查集

#include <iostream>  
#include <cstring>  
#include <cstdio>  
#include <algorithm>  
#include <cmath>  
#include <cstdlib>  
#include <limits>  
#include <vector>  
#include <sstream>  
#define ll long long  
  
using namespace std;  
  
const int maxn = 2000 + 10;  
const int maxm = 1000000 + 10;  
const int inf = 0x3f3f3f3f;  
  
int father[maxn];  
int r[maxn];  
  
int n, m, k;  
  
void init() {  
    for(int i = 0; i <= n; i++) {  
        father[i] = i;  
        r[i] = 0;  
    }  
}  
  
int getf(int x) {  
    int t;  
    if(father[x] != x) {  
        t = getf(father[x]);  
        r[x] = (r[x] + r[father[x]]) % 2;  
        return father[x] = t;  
    }  
    return x;  
}  
  
int main()  
{  
    int kcase = 0;  
    int T;  
    scanf("%d", &T);  
    while(T--) {  
        scanf("%d %d", &n, &m);  
        init();  
        int flag = 1;  
        while(m--) {  
            int a, b;  
            scanf("%d %d", &a, &b);  
            int x = getf(a), y = getf(b);  
            if(x == y) {  
                if((r[a] + r[b]) % 2 == 0) {  
                    flag = 0;  
                }  
            }  
            else {  
                father[x] = y;  
                r[x] = (r[a] + r[b] + 1) % 2;  
            }  
        }  
        if(kcase) puts("");  
        printf("Scenario #%d:\n", ++kcase);  
        if(flag) puts("No suspicious bugs found!");  
        else puts("Suspicious bugs found!");  
    }  
    return 0;  
}  

六、线段树

1、点更新

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#include <deque>  
#define INF 0x3f3f3f3f  
#define ll long long  
  
using namespace std;  
  
const int inf = 0x3f3f3f3f;  
const int maxn = 50005;  
const int maxm = 1000005;  
  
int n, m, a[maxn];  
  
struct node {  
    int l, r;  
    int v;  
}tree[maxn << 2];  
  
void pushup(int rt) {  
    tree[rt].v = tree[rt << 1].v + tree[rt << 1 | 1].v;  
}  
  
void build(int rt, int l, int r) {  
    tree[rt].l = l;  
    tree[rt].r = r;  
    if(r == l) {  
        tree[rt].v = a[l];  
        return;  
    }  
    int mid = (l + r) >> 1;  
    build(rt << 1, l, mid);  
    build(rt << 1 | 1, mid + 1, r);  
    pushup(rt);  
}  
  
void update(int rt, int k, int v) {  
    if(tree[rt].l == k && tree[rt].r == k) {  
        tree[rt].v += v;  
        return;  
    }  
    int mid = (tree[rt].l + tree[rt].r) >> 1;  
    if(k <= mid)  
        update(rt << 1, k, v);  
    if(k > mid)  
        update(rt << 1 | 1, k, v);  
    pushup(rt);  
}  
  
int query(int rt, int l, int r) {  
    if(tree[rt].l == l && tree[rt].r == r)  
        return tree[rt].v;  
    int mid = (tree[rt].l + tree[rt].r) >> 1;  
    int res = 0;  
    if(r <= mid)  
        res += query(rt << 1, l, r);  
    if(l > mid)  
        res += query(rt << 1 | 1, l, r);  
    if(l <= mid && r > mid)  
        res += query(rt << 1, l, mid) + query(rt << 1 | 1, mid + 1, r);  
  
    return res;  
}  
  
int main ()  
{  
    int T, kcase = 0;  
    scanf("%d", &T);  
    while(T--) {  
        scanf("%d", &n);  
        for(int i = 1; i <= n; i++) {  
            scanf("%d", &a[i]);  
        }  
        build(1, 1, n);  
        int b, c;  
        char str[5];  
        printf("Case %d:\n", ++kcase);  
        while(~scanf("%s", str)) {  
            if(str[0] == 'E') break;  
            scanf("%d %d", &b, &c);  
            if(str[0] == 'S') {  
                update(1, b, -c);  
            }  
            else if(str[0] == 'A') {  
                update(1, b, c);  
            }  
            else {  
                printf("%d\n", query(1, b, c));  
            }  
        }  
    }  
    return 0;  
}  

2、区间更新

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#include <deque>  
#include <sstream>  
#define INF 0x3f3f3f3f  
using namespace std;  
  
const int maxn = 100010;  
const int inf = 0x3f3f3f3f;  
  
struct node {  
    int l, r;  
    long long sum, add;  
}tree[maxn << 2];  
  
void pushup(int rt) {  
    tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;  
}  
  
void pushdown(int rt, int len) {  
    if(tree[rt].add) {  
        tree[rt << 1].add += tree[rt].add;  
        tree[rt << 1 | 1].add += tree[rt].add;  
        tree[rt << 1].sum += tree[rt].add * (len - (len >> 1));  
        tree[rt << 1 | 1].sum += tree[rt].add * (len >> 1);  
        tree[rt].add = 0;  
    }  
}  
  
void build(int rt, int l, int r) {  
    tree[rt].l = l;  
    tree[rt].r = r;  
    tree[rt].add = 0;  
    if(l == r) {  
        scanf("%lld", &tree[rt].sum);  
        return;  
    }  
    int mid = (tree[rt].l + tree[rt].r) >> 1;  
    build(rt << 1, l, mid);  
    build(rt << 1 | 1, mid + 1, r);  
    pushup(rt);  
}  
  
void update(int rt, int l, int r, int c)  
{  
    if(tree[rt].l == l && tree[rt].r == r) {  
        tree[rt].add += c;  
        tree[rt].sum += (long long)c * (r - l + 1);  
        return;  
    }  
    if(tree[rt].l == tree[rt].r)  
        return;  
    pushdown(rt, tree[rt].r - tree[rt].l + 1);  
    int mid = (tree[rt].l + tree[rt].r) >> 1;  
    if(r <= mid)  
        update(rt << 1, l, r, c);  
    else if(l > mid)  
        update(rt << 1 | 1, l, r, c);  
    else {  
        update(rt << 1, l, mid, c);  
        update(rt << 1 | 1, mid + 1, r, c);  
    }  
    pushup(rt);  
}  
  
long long query(int rt, int l, int r) {  
    if(tree[rt].l == l && tree[rt].r == r)  
        return tree[rt].sum;  
    pushdown(rt, tree[rt].r - tree[rt].l + 1);  
    int mid = (tree[rt].r + tree[rt].l) >> 1;  
    long long res = 0;  
    if(r <= mid)  
        res += query(rt << 1, l, r);  
    else if(l > mid)  
        res += query(rt << 1 | 1, l, r);  
    else  
        res += query(rt << 1, l, mid) + query(rt << 1 | 1, mid + 1, r);  
    return res;  
  
}  
  
int main()  
{  
    int n, m;  
    while(scanf("%d %d", &n, &m) != EOF) {  
        build(1, 1, n);  
        while(m--) {  
            char ch[2];  
            scanf("%s", ch);  
            int a, b, c;  
            if(ch[0] == 'Q') {  
                scanf("%d %d", &a, &b);  
                printf("%lld\n", query(1, a, b));  
            }  
            else {  
                scanf("%d %d %d", &a, &b, &c);  
                update(1, a, b, c);  
            }  
        }  
    }  
    return 0;  
}  

七、强双联通

1、强连通分量tarjan

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
#define INF 0x3f3f3f3f  
#define ll long long  
  
using namespace std;  
  
const int inf = 0x3f3f3f3f;  
const int maxn = 10005;  
const int maxm = 100005;  
  
struct node {  
    int v, next;  
}edge[maxm];  
  
int sstack[maxn], belong[maxn];  
int dfn[maxn], low[maxn];  
int head[maxn];  
bool instack[maxn];  
int cnt, top, tot, scnt;  
int n, m;  
  
void init() {  
    cnt = 0;  
    top = tot = scnt = 0; //栈顶指针为0 ,次序计数器,初始化连通分量标号  
    memset(head, -1, sizeof(head));  
    memset(dfn, 0, sizeof(dfn)); //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用  
}  
  
void addedge(int u, int v) {  
    edge[tot].v = v;  
    edge[tot].next = head[u];  
    head[u] = tot++;  
}  
  
void tarjan(int u) { //Tarjan算法求有向图的强连通分量  
    dfn[u] = low[u] = ++cnt; //cnt为时间戳  
    instack[u] = 1; //标记在栈中  
    sstack[top++] = u; //入栈  
    for(int e = head[u]; e != -1; e = edge[e].next) { //枚举u的每一条边  
        int v = edge[e].v; //u所邻接的边  
        if(!dfn[v]) { //未被访问  
            tarjan(v); //继续向下找  
            if(low[u] > low[v]) low[u] = low[v]; // 更新结点u所能到达的最小次数层  
        }  
        else if(instack[v] && low[u] > dfn[v]) { //如果v结点在栈内  
            low[u] = dfn[v];  
        }  
    }  
    if(low[u] == dfn[u]) { //如果节点u是强连通分量的根  
        scnt++; //连通分量标号加1  
        while(1) {  
            int v = sstack[--top]; //退栈  
            instack[v] = 0; //标记不在栈中  
            belong[v] = scnt; //出栈结点t属于scnt标号的强连通分量  
            if(v == u) break; //直到将u从栈中退出  
        }  
    }  
}  
  
int main()  
{  
    int T, kcase = 0;  
    while(~scanf("%d %d", &n, &m) && (n || m)) {  
        init();  
        int a, b;  
        while(m--) {  
            scanf("%d %d", &a, &b);  
            addedge(a, b);  
        }  
        for(int i = 1; i <= n; i++) { //枚举每个结点,搜索连通分量  
            if(!dfn[i]) { //未被访问  
                tarjan(i); //则找i结点的连通分量  
            }  
        }  
        if(scnt == 1) {  
            puts("Yes");  
        }  
        else {  
            puts("No");  
        }  
    }  
    return 0;  
}  

八、欧拉图

1、无向(半)欧拉图判定

#include <iostream>  
#include <algorithm>  
#include <cstdio>  
#include <cmath>  
#include <cstring>  
#include <string>  
#include <set>  
#include <map>  
#include <stack>  
#include <queue>  
  
using namespace std;  
  
const int maxn = 10005;  
const int maxm = 1000050;  
const int inf = 0x7f7f7f7f;  
  
int n, m;  
int fa[maxn];  
int degree[maxn];  
  
int findset(int x) {  
    if(fa[x] == -1) {  
        return x;  
    }  
    return fa[x] = findset(fa[x]);  
}  
  
int main()  
{  
    while(scanf("%d", &n) && n) {  
        scanf("%d", &m);  
        memset(degree, 0, sizeof(degree));  
        memset(fa, -1, sizeof(fa));  
        for(int i = 1; i <= m; i++) {  
            int a, b;  
            scanf("%d %d", &a, &b);  
            degree[a]++;  
            degree[b]++;  
            a = findset(a);  
            b = findset(b);  
            if(a != b) {  
                fa[a] = b;  
            }  
        }  
        int cnt = 0;  
        for(int i = 1; i <= n; i++) {  
            if(findset(i) == i) cnt++;  
        }  
        if(cnt > 1) {  
            puts("0");  
            continue;  
        }  
        cnt = 0;  
        for(int i = 1; i <= n; i++) {  
            if(degree[i] % 2) cnt++;  
        }  
        if(cnt != 0) puts("0");  
        else puts("1");  
    }  
    return 0;  
}  

2、有向(半)欧拉图判定

#include<cstdio>  
#include<cstring>  
using namespace std;  
const int maxn=26+5;  
int fa[maxn];  
int in[maxn],out[maxn];  
int m;//单词数  
int findset(int u)  
{  
    if(fa[u]==-1) return u;  
    return fa[u]=findset(fa[u]);  
}  
int main()  
{  
    int T; scanf("%d",&T);  
    while(T--)  
    {  
        memset(in,0,sizeof(in));  
        memset(out,0,sizeof(out));  
        memset(fa,-1,sizeof(fa));  
        scanf("%d",&m);  
        for(int i=0;i<m;i++)  
        {  
            char str[1200];  
            scanf("%s",str);  
            int len=strlen(str);  
            int u=str[0]-'a', v=str[len-1]-'a';  
            in[u]++;  
            out[v]++;  
            u=findset(u), v=findset(v);  
            if(u!=v) fa[u]=v;  
        }  
        int cnt=0;  
        for(int i=0;i<26;i++)  
            if( (in[i]||out[i]) && findset(i)==i ) cnt++;  
        if(cnt>1)  
        {  
            printf("The door cannot be opened.\n");  
            continue;  
        }  
        int c1=0, c2=0, c3=0;//分别表示入度!=出度时的三种情况  
        for(int i=0;i<26;i++)  
        {  
            if(in[i]==out[i]) continue;  
            else if(in[i]-out[i]==1) c1++;  
            else if(out[i]-in[i]==1) c2++;  
            else c3++;  
        }  
        if( ( (c1==c2&&c1==1)||(c1==c2&&c1==0) )&&c3==0 )  
            printf("Ordering is possible.\n");  
        else  
            printf("The door cannot be opened.\n");  
    }  
    return 0;  
}  

3、无向(半)欧拉图输出欧拉回路或通路

#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
const int maxn=100+5;  
  
//无向图打印欧拉路径或回路  
//输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的无向图  
  
int n;//图中顶点,顶点编号1到n  
int m;//图中边  
int G[maxn][maxn];  
int vis[maxn][maxn];//vis[i][j]==1表示i与j点直接存在一条边  
  
//打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)  
//打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的  
//如果是打印欧拉路径,那么输入的u一定要是起点之一,即度数为奇数的点之一  
//否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已  
void euler(int u)  
{  
    for(int v=1;v<=n;v++)if(vis[u][v]||vis[v][u])  
    {  
        //递归思想,去掉了u与v这条边,  
        //余图还是一个具有欧拉道路的图,且v变成一个起点了  
        vis[u][v]=vis[v][u]=0;  
        euler(v);  
        printf("%d %d\n",u,v);  
    }  
}  
  
//输出欧拉回路或路径上按顺序经过的节点  
//u也必须要是起点之一,否则输出的也是乱序点而已  
void euler_point(int u)  
{  
    for(int v=1;v<=n;v++)if(vis[u][v] || vis[v][u])  
    {  
        vis[u][v]=vis[v][u]=0;  
        euler_point(v);  
    }  
    printf("%d\n",u);  
}  
  
int main()  
{  
    while(scanf("%d%d",&n,&m)==2)  
    {  
        memset(G,0,sizeof(G));  
        memset(vis,0,sizeof(vis));  
        for(int i=1;i<=m;i++)  
        {  
            int u,v;  
            scanf("%d%d",&u,&v);  
            G[u][v]=G[v][u]=1;//无向图  
            vis[u][v]=vis[v][u]=1;  
        }  
  
        int u;  
        scanf("%d",&u);  
        euler_point(u);  
  
    }  
    return 0;  
}  

4、有向(半)欧拉图输出欧拉回路或通路

#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
const int maxn=100+5;  
  
//有向图打印欧拉路径或回路  
//输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的有向图  
  
int n;//图中顶点,顶点编号1到n  
int m;//图中边  
int G[maxn][maxn];  
int vis[maxn][maxn];//vis[i][j]==1表示i到j点存在一条边  
  
//打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)  
//打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的  
//如果是打印欧拉路径,那么输入的u一定要是起点之一,即abs(入度-出度)==1  
//且如果是出度-入度==1的点,那么该点就应该是欧拉图的起点,但是会逆序输出,所以最后才输出  
//否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已  
void euler(int u)  
{  
    for(int v=1;v<=n;v++)if(vis[u][v])  
    {  
        //递归思想,去掉了u与v这条边,  
        //余图还是一个具有欧拉道路的图,且v变成一个起点了  
        vis[u][v]=0;  
        euler(v);  
        printf("%d %d\n",u,v);  
    }  
}  
  
//逆序输出欧拉回路或路径上按顺序经过的节点  
//u也必须要是起点之一,否则输出的也是乱序点而已  
void euler_point(int u)  
{  
    for(int v=1;v<=n;v++)if(vis[u][v])  
    {  
        vis[u][v]=0;  
        euler_point(v);  
    }  
    printf("%d\n",u);  
}  
  
int main()  
{  
    while(scanf("%d%d",&n,&m)==2)  
    {  
        memset(G,0,sizeof(G));  
        memset(vis,0,sizeof(vis));  
        for(int i=1;i<=m;i++)  
        {  
            int u,v;  
            scanf("%d%d",&u,&v);  
            G[u][v]=1;//有向图  
            vis[u][v]=1;  
        }  
  
        int u;  
        scanf("%d",&u);  
        euler(u);  
  
    }  
    return 0;  
}  

猜你喜欢

转载自blog.csdn.net/c_cqq/article/details/80159456