ACM算法模板2

涵盖了ACM竞赛的大多数常用算法,算法较多可以直接搜索查询

Floyd求最小环

e(u, v)表示u和v之间的连边,令min(u, v)表示删除u和v之间的连边之后u和v之间的最短路, 最小环则是min(u, v) + e(u, v). 时间复杂度是 O(EV^2). 
改进算法 
在floyd的同时,顺便算出最小环 
g[i][j]=i, j之间的边长


dist:=g;
for k:=1 to n do
begin
    for i:=1 to k-1 do
      for j:=i+1 to k-1 do
        answer:=min(answer, dist[i][j]+g[i][k]+g[k][j]);
    for i:=1 to n do
      for j:=1 to n do
        dist[i][j]:=min(dist[i][j], dist[i][k]+dist[k][j]);
end;

最小环改进算法的证明 
一个环中的最大结点为k(编号最大), 与他相连的两个点为i, j, 这个环的最短长度为g[i][k]+g[k][j]+i到j的路径中所有结点编号都小于k的最短路径长度. 根据floyd的原理, 在最外层循环做了k-1次之后, dist[i][j]则代表了i到j的路径中所有结点编号都小于k的最短路径综上所述,该算法一定能找到图中最小环。

const int INF = 0x3f3f3f3f;
const int MAXN = 110;

int n, m;               //  n:节点个数, m:边的个数
int g[MAXN][MAXN];      //  无向图
int dist[MAXN][MAXN];   //  最短路径
int r[MAXN][MAXN];      //  r[i][j]: i到j的最短路径的第一步
int out[MAXN], ct;      //  记录最小环

int solve(int i, int j, int k)
{   //  记录最小环
    ct = 0;
    while (j != i)
    {
        out[ct++] = j;
        j = r[i][j];
    }
    out[ct++] = i;
    out[ct++] = k;
    return 0;
}

int main()
{
    while (scanf("%d%d", &n, &m) != EOF)
    {
        int i, j, k;
        for (i = 0; i < n; i++)
        {
            for (j = 0; j < n; j++)
            {
                g[i][j] = INF;
                r[i][j] = i;
            }
        }
        for (i = 0; i < m; i++)
        {
            int x, y, l;
            scanf("%d%d%d", &x, &y, &l);
            --x;
            --y;
            if (l < g[x][y])
            {
                g[x][y] = g[y][x] = l;
            }
        }
        memmove(dist, g, sizeof(dist));
        int Min = INF;              //  最小环
        for (k = 0; k < n; k++)
        {                           //  Floyd
            for (i = 0; i < k; i++) //  一个环中的最大结点为k(编号最大)
            {
                if (g[k][i] < INF)
                {
                    for (j = i + 1; j < k; j++)
                    {
                        if (dist[i][j] < INF && g[k][j] < INF && Min > dist[i][j] + g[k][i] + g[k][j])
                        {
                            Min = dist[i][j] + g[k][i] + g[k][j];
                            solve(i, j, k);     //  记录最小环
                        }
                    }
                }
            }
            for (i = 0; i < n; i++)
            {
                if (dist[i][k] < INF)
                {
                    for (j = 0; j < n; j++)
                    {
                        if (dist[k][j] < INF && dist[i][j] > dist[i][k]+dist[k][j])
                        {
                            dist[i][j] = dist[i][k] + dist[k][j];
                            r[i][j] = r[k][j];
                        }
                    }
                }
            }
        }
        if (Min < INF)
        {
            for (ct--; ct >= 0; ct--)
            {
                printf("%d", out[ct] + 1);
                if (ct)
                {
                    printf(" ");
                }
            }
        }
        else
        {
            printf("No solution.");
        }
        printf("\n");
    }

    return 0;
}

2-SAT问题

/*
 *  2-sat 问题
 *  N个集团,每个集团2个人,现在要想选出尽量多的人,
 *  且每个集团只能选出一个人。如果两人有矛盾,他们不能同时被选中
 *  问最多能选出多少人
 */
const int MAXN = 3010;
int n, m;
int g[3010][3010], ct[3010], f[3010];
int x[3010], y[3010];
int prev[MAXN], low[MAXN], stk[MAXN], sc[MAXN];
int cnt[MAXN];
int cnt0, ptr, cnt1;
void dfs(int w)
{
    int min(0);
    prev[w] = cnt0++;
    low[w] = prev[w];
    min = low[w];
    stk[ptr++] = w;
    for (int i = 0; i < ct[w]; ++i)
    {
        int t = g[w][i];
        if (prev[t] == -1)
        {
            dfs(t);
        }
        if (low[t] < min)
        {
            min = low[t];
        }
    }
    if (min < low[w])
    {
        low[w] = min;
        return ;
    }
    do
    {
        int v = stk[--ptr];
        sc[v] = cnt1;
        low[v] = MAXN;
    } while(stk[ptr] != w);
    ++cnt1;
    return ;
}

void Tarjan(int N)
{   //  传入N为点数,结果保存在sc数组中,同一标号的点在同一个强连通分量内,
    //  强连通分量数为cnt1
    cnt0 = cnt1 = ptr = 0;
    int i;
    for (i = 0; i < N; ++i)
    {
        prev[i] = low[i] = -1;
    }
    for (i = 0; i < N; ++i)
    {
        if (prev[i] == -1)
        {
            dfs(i);
        }
    }
    return ;
}

int solve()
{
    Tarjan(n);
    for (int i = 0; i < n; i++)
    {
        if (sc[i] == sc[f[i]])
        {
            return 0;
        }
    }
    return 1;
}

int check(int Mid)
{
    for (int i = 0; i < n; i++)
    {
        ct[i] = 0;
    }
    for (int i = 0; i < Mid; i++)
    {
        g[f[x[i]]][ct[f[x[i]]]++] = y[i];
        g[f[y[i]]][ct[f[y[i]]]++] = x[i];
    }
    return solve();
}

int main()
{
    while (scanf("%d%d", &n, &m) != EOF && n + m)
    {
        for (int i = 0; i < n; i++)
        {
            int p, q;
            scanf("%d%d", &p, &q);
            f[p] = q, f[q] = p;
        }
        for (int i = 0; i < m; i++)
        {
            scanf("%d%d", &x[i], &y[i]);
        }
        n *= 2;
        int Min = 0, Max = m + 1;
        while (Min + 1 < Max)
        {
            int Mid = (Min + Max) / 2;
            if (check(Mid))
            {
                Min = Mid;
            }
            else
            {
                Max = Mid;
            }
        }
        printf("%d\n", Min);
    }
    return 0;
}

树的重心

typedef long long ll;
typedef pair<int, int> pll;

const int INF = 0x3f3f3f3f;
const int MAXN = 100000 + 10;

int n;

/*  树的重心
 *  初始化 vis[] son[] 为 0
 *  初始化 sz 为 INF
 */
int zx, sz;
int son[MAXN], vis[MAXN];
vector<pll> edge[MAXN];

void init()
{
    for (int i = 1; i <= n; i++)
    {
        edge[i].clear();
    }
    memset(vis, 0, sizeof(vis));
    sz = INF;
    zx = -1;
}

void dfs(int r)
{
    vis[r] = 1;
    son[r] = 0;
    int tmp = 0;
    for (int i = 0; i < edge[r].size(); i++)
    {
        int v = edge[r][i].second;
        if (!vis[v])
        {
            dfs(v);
            son[r] += son[v] + 1;
            tmp = max(tmp, son[v] + 1);
        }
    }
    tmp = max(tmp, n - son[r] - 1);
    if (tmp < sz)
    {
        zx = r;
        sz = tmp;
    }
}

最小生成树-Kruskal

void Kruskal() {    
    ans = 0;    
    for (int i = 0; i<len; i++) {    
        if (Find(edge[i].a) != Find(edge[i].b)) {    
            Union(edge[i].a, edge[i].b);    
            ans += edge[i].len;    
        }    
    }    
}    

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=10000+5;
int pre[maxn];
int n,len;
struct node{
    int u,v,w;
}stu[maxn];
bool cmp(node x,node y)
{
    return x.w<y.w;
}
int find_pre(int x)
{
    if(x!=pre[x])
    pre[x]=find_pre(pre[x]);
    return pre[x];
}
int join(int a,int b)
{
    int x=find_pre(a);
    int y=find_pre(b);
    if(x!=y)
    {
        pre[x]=y;
        return 1;
    }
    return 0;
}
void Kruskal(){
    int cnt=0,sum=0;
    for(int i=1;i<=n;i++)
        pre[i]=i;
    for(int i=0;i<len;i++){
        if(join(stu[i].u,stu[i].v)){
            ++cnt;
            sum+=stu[i].w;
        }
        if(cnt==n-1)
            break;
    }
    cout<<sum<<endl;
}
int main(){
    char s,s2;
    int w,m;
    while(cin>>n&&n){
        len=0;
        memset(pre,0,sizeof(pre));
        for(int i=1;i<=n-1;i++)
        {
            getchar();
            scanf("%c ",&s);
            cin>>m;
            for(int j=0;j<m;j++){
                stu[len].u=s-'A';
                scanf(" %c %d",&s2,&w);
                stu[len].v=s2-'A';
                stu[len++].w=w;
            }
        }
        sort(stu,stu+len,cmp);
        Kruskal();
    }
    return 0;
}

最小生成树-Prim

/*
    |Prim算法|
    |适用于 稠密图 求最小生成树|
    |堆优化版,时间复杂度:O(elgn)|
*/

struct node {  
    int v, len;  
    node(int v = 0, int len = 0) :v(v), len(len) {}  
    bool operator < (const node &a)const {  // 加入队列的元素自动按距离从小到大排序  
        return len> a.len;  
    }  
};

vector<node> G[maxn];
int vis[maxn];
int dis[maxn];

void init() {  
    for (int i = 0; i<maxn; i++) {  
        G[i].clear();  
        dis[i] = INF;  
        vis[i] = false;  
    }  
}  
int Prim(int s) {  
    priority_queue<node>Q; // 定义优先队列  
    int ans = 0;  
    Q.push(node(s,0));  // 起点加入队列  
    while (!Q.empty()) {   
        node now = Q.top(); Q.pop();  // 取出距离最小的点  
        int v = now.v;  
        if (vis[v]) continue;  // 同一个节点,可能会推入2次或2次以上队列,这样第一个被标记后,剩下的需要直接跳过。  
        vis[v] = true;  // 标记一下  
        ans += now.len;  
        for (int i = 0; i<G[v].size(); i++) {  // 开始更新  
            int v2 = G[v][i].v;  
            int len = G[v][i].len;  
            if (!vis[v2] && dis[v2] > len) {   
                dis[v2] = len;  
                Q.push(node(v2, dis[v2]));  // 更新的点加入队列并排序  
            }  
        }  
    }  
    return ans; 
}  

单源最短路径-Dijkstra

/*
    |Dijkstra算法|
    |适用于边权为正的有向图或者无向图|
    |求从单个源点出发,到所有节点的最短路|
    |优化版:时间复杂度 O(elbn)|
*/

struct node {  
    int v, len;  
    node(int v = 0, int len = 0) :v(v), len(len) {}  
    bool operator < (const node &a)const {  //  距离从小到大排序  
        return len > a.len;  
    }  
};  

vector<node>G[maxn];  
bool vis[maxn];  
int dis[maxn];

void init() {  
    for (int i = 0; i<maxn; i++) {  
        G[i].clear();  
        vis[i] = false;  
        dis[i] = INF;  
    }  
}  
int dijkstra(int s, int e) {  
    priority_queue<node>Q;  
    Q.push(node(s, 0)); //  加入队列并排序  
    dis[s] = 0;  
    while (!Q.empty()) {  
        node now = Q.top();     //  取出当前最小的  
        Q.pop();  
        int v = now.v;  
        if (vis[v]) continue;   //  如果标记过了, 直接continue  
        vis[v] = true;  
        for (int i = 0; i<G[v].size(); i++) {   //  更新  
            int v2 = G[v][i].v;  
            int len = G[v][i].len;  
            if (!vis[v2] && dis[v2] > dis[v] + len) {  
                dis[v2] = dis[v] + len;  
                Q.push(node(v2, dis[v2]));  
            }  
        }  
    }  
    return dis[e];  
}  

最短路径快速算法-SPFA

/*
    |Dijkstra算法|
    |适用于边权为正的有向图或者无向图|
    |求从单个源点出发,到所有节点的最短路|
    |优化版:时间复杂度 O(elbn)|
*/

struct node {  
    int v, len;  
    node(int v = 0, int len = 0) :v(v), len(len) {}  
    bool operator < (const node &a)const {  //  距离从小到大排序  
        return len > a.len;  
    }  
};  

vector<node>G[maxn];  
bool vis[maxn];  
int dis[maxn];

void init() {  
    for (int i = 0; i<maxn; i++) {  
        G[i].clear();  
        vis[i] = false;  
        dis[i] = INF;  
    }  
}  
int dijkstra(int s, int e) {  
    priority_queue<node>Q;  
    Q.push(node(s, 0)); //  加入队列并排序  
    dis[s] = 0;  
    while (!Q.empty()) {  
        node now = Q.top();     //  取出当前最小的  
        Q.pop();  
        int v = now.v;  
        if (vis[v]) continue;   //  如果标记过了, 直接continue  
        vis[v] = true;  
        for (int i = 0; i<G[v].size(); i++) {   //  更新  
            int v2 = G[v][i].v;  
            int len = G[v][i].len;  
            if (!vis[v2] && dis[v2] > dis[v] + len) {  
                dis[v2] = dis[v] + len;  
                Q.push(node(v2, dis[v2]));  
            }  
        }  
    }  
    return dis[e];  
}  

弗洛伊德算法-Floyd-Warshall

/*
    |Floyd算法|
    |任意点对最短路算法|
    |求图中任意两点的最短距离的算法|
*/

for (int i = 0; i < n; i++) {   //  初始化为0  
    for (int j = 0; j < n; j++)  
        scanf("%lf", &dis[i][j]);  
}  
for (int k = 0; k < n; k++) {  
    for (int i = 0; i < n; i++) {  
        for (int j = 0; j < n; j++) {  
            dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);  
        }  
    }
}

Network 网络流

二分图匹配相关

匈牙利算法(邻接矩阵+DFS)

/*
 *  初始化:g[][]两边顶点的划分情况 
 *  建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配 
 *  g没有边相连则初始化为0
 *  uN是匹配左边的顶点数,vN是匹配右边的顶点数 
 *  调用:res=hungary();输出最大匹配数 
 *  优点:适用于稠密图,DFS找增广路,实现简洁易于理解
 *  时间复杂度:O(VE) 
 */
//顶点编号从0开始的
const int MAXN = 510;
int uN, vN;         //  u,v的数目,使用前面必须赋值
int g[MAXN][MAXN];  //  邻接矩阵
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)
{
    for (int 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;
    memset(linker, -1, sizeof(linker));
    for (int u = 0; u < uN; u++)
    {
        memset(used, false, sizeof(used));
        if (dfs(u))
        {
            res++;
        }
    }
    return res;
}

匈牙利算法(邻接表+DFS)

/*
 *  使用前用init()进行初始化,给uN赋值
 *  加边使用函数addedge(u,v)
 */
const int MAXN = 5010;  //  点数的最大值
const int MAXM = 50010; //  边数的最大值

struct Edge
{
    int to, next;
} edge[MAXM];

int head[MAXN], tot;

void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
    return ;
}

void addedge(int u, int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
    return ;
}

int linker[MAXN];
bool used[MAXN];
int uN;

bool dfs(int u)
{
    for (int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if (!used[v])
        {
            used[v] = true;
            if (linker[v] == -1 || dfs(linker[v]))
            {
                linker[v] = u;
                return true;
            }
        }
    }
    return false;
}

int hungary()
{
    int res = 0;
    memset(linker, -1, sizeof(linker));
    for (int u = 0; u < uN; u++)    //  点的编号0~uN-1
    {
        memset(used, false, sizeof(used));
        if (dfs(u))
        {
            res++;
        }
    }
    return res;
}

匈牙利算法(邻接矩阵+BFS)

/*
 *  INIT: g[][]邻接矩阵;
 *  CALL: res = MaxMatch();Nx, Ny初始化!!! 
 *  优点:适用于稀疏二分图,边较少,增广路较短。
 *  匈牙利算法的理论复杂度是O(VE)
 */
const int MAXN = 1000;
int g[MAXN][MAXN], Mx[MAXN], My[MAXN], Nx, Ny;
int chk[MAXN], Q[MAXN], prev[MAXN];

int MaxMatch()
{
    int res = 0;
    int qs, qe;
    memset(Mx, -1, sizeof(Mx));
    memset(My, -1, sizeof(My));
    memset(chk, -1, sizeof(chk));
    for (int i = 0; i < Nx; i++)
    {
        if (Mx[i] == -1)
        {
            qs = qe = 0;
            Q[qe++] = i;
            prev[i] = -1;
            bool flag = 0;
            while (qs < qe && !flag)
            {
                int u = Q[qs];
                for (int v = 0; v < Ny && !flag; v++)
                {
                    if (g[u][v] && chk[v] != i)
                    {
                        chk[v] = i; Q[qe++] = My[v];
                        if (My[v] >= 0)
                        {
                            prev[My[v]] = u;
                        }
                        else
                        {
                            flag = 1;
                            int d = u, e = v;
                            while (d != -1)
                            {
                                int t = Mx[d];
                                Mx[d] = e;
                                My[e] = d;
                                d = prev[d];
                                e = t;
                            }
                        }
                    }
                }
                qs++;
            }
            if (Mx[i] != -1)
            {
                res++;
            }
        }
    }
    return res;
}

Hopcroft-Carp算法(邻接矩阵+DFS)

/*
 *  INIT: g[][]邻接矩阵;
 *  CALL: res = MaxMatch(); Nx, Ny要初始化!!!
 *  时间复杂度: O(V^0.5 * E)
 */
const int MAXN = 3001;
const int INF = 1 << 28;
int g[MAXN][MAXN], Mx[MAXN], My[MAXN], Nx, Ny;
int dx[MAXN], dy[MAXN], dis;
bool vst[MAXN];

bool searchP()
{
    queue<int> Q;
    dis = INF;
    memset(dx, -1, sizeof(dx));
    memset(dy, -1, sizeof(dy));
    for (int i = 0; i < Nx; i++)
    {
        if (Mx[i] == -1)
        {
            Q.push(i); dx[i] = 0;
        }
    }
    while (!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if (dx[u] > dis)
        {
            break;
        }
        for (int v = 0; v < Ny; v++)
        {
            if (g[u][v] && dy[v] == -1)
            {
                dy[v] = dx[u]+1;
                if (My[v] == -1)
                {
                    dis = dy[v];
                }
                else
                {
                    dx[My[v]] = dy[v] + 1;
                    Q.push(My[v]);
                }
            }
        }
    }
    return dis != INF;
}

bool DFS(int u)
{
    for (int v = 0; v < Ny; v++)
    {
        if (!vst[v] && g[u][v] && dy[v] == dx[u] + 1)
        {
            vst[v] = 1;
            if (My[v] != -1 && dy[v] == dis)
            {
                continue;
            }
            if (My[v] == -1 || DFS(My[v]))
            {
                My[v] = u; Mx[u] = v;
                return 1;
            }
        }
    }
    return 0;
}

int MaxMatch()
{
    int res = 0;
    memset(Mx, -1, sizeof(Mx));
    memset(My, -1, sizeof(My));
    while (searchP())
    {
        memset(vst, 0, sizeof(vst));
        for (int i = 0; i < Nx; i++)
        {
            if (Mx[i] == -1 && DFS(i))
            {
                res++;
            }
        }
    }
    return res;
}

Hopcroft-Carp算法(邻接表+DFS)

/*
 *  复杂度O(sqrt(n)*E)
 *  邻接表存图,vector实现
 *  vector先初始化,然后假如边
 *  uN为左端的顶点数,使用前赋值(点编号0开始)
 */
const int MAXN = 3000;
const int INF = 0x3f3f3f3f;
vector<int>G[MAXN];
int uN;
int Mx[MAXN], My[MAXN];
int dx[MAXN], dy[MAXN];
int dis;
bool used[MAXN];

bool SearchP()
{
    queue<int>Q;
    dis = INF;
    memset(dx, -1, sizeof(dx));
    memset(dy, -1, sizeof(dy));
    for (int i = 0 ; i < uN; i++)
    {
        if(Mx[i] == -1)
        {
            Q.push(i);
            dx[i] = 0;
        }
    }
    while (!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if (dx[u] > dis)
        {
            break;
        }
        int sz = (int)G[u].size();
        for (int i = 0; i < sz; i++)
        {
            int v = G[u][i];
            if (dy[v] == -1)
            {
                dy[v] = dx[u] + 1;
                if (My[v] == -1)
                {
                    dis = dy[v];
                }
                else
                {
                    dx[My[v]] = dy[v] + 1;
                    Q.push(My[v]);
                }
            }
        }
    }
    return dis != INF;
}

bool DFS(int u)
{
    int sz = (int)G[u].size();
    for (int i = 0; i < sz; i++)
    {
        int v = G[u][i];
        if (!used[v] && dy[v] == dx[u] + 1)
        {
            used[v] = true;
            if (My[v] != -1 && dy[v] == dis)
            {
                continue;
            }
            if (My[v] == -1 || DFS(My[v]))
            {
                My[v] = u;
                Mx[u] = v;
                return true;
            }
        }
    }
    return false;
}

int MaxMatch()
{
    int res = 0;
    memset(Mx, -1, sizeof(Mx));
    memset(My, -1, sizeof(My));
    while (SearchP())
    {
        memset(used, false, sizeof(used));
        for (int i = 0; i < uN; i++)
        {
            if(Mx[i] == -1 && DFS(i))
            {
                res++;
            }
        }
    }
    return res;
}

二分图最佳匹配

Kuhn Munkras算法

/*
 *  邻接距阵形式,复杂度O(m*m*n) 返回最佳匹配值,传入二分图大小m,n 
 *  邻接距阵mat,表示权,match1,match2返回一个最佳匹配,未匹配顶点
 *  match值为-1,一定注意m<=n,否则循环无法终止,最小权匹配可将权值 
 *  取相反数
 *  初始化:for (i = 0; i < MAXN; ++i)
 *          for (j = 0; j < MAXN ; ++j)
 *              mat[i][j] = -inf; 
 *  对于存在的边:mat[i][j] = val ;    //  注意,不能有负值
 */
#define MAXN 310
#define inf 1000000000
#define _clr(x) memset(x, -1, sizeof(int) * MAXN)

int kuhn_munkras(int m, int n, int mat[][MAXN], int *match_1, int *match_2)
{
    int s[MAXN], t[MAXN], l_1[MAXN], l_2[MAXN];
    int p, q, ret = 0;
    int i, j, k;
    for (i = 0; i < m; i++)
    {
        for (l_1[i] = -inf, j = 0; j < n; j++)
        {
            l_1[i] = mat[i][j] > l_1[i] ? mat[i][j] : l_1[i];
        }
        if (l_1[i] == -inf)
        {
            return -1;  //  无结果
        }
    }
    for (i = 0; i < n; l_2[i++] = 0);
    for (_clr(match_1), _clr(match_2), i = 0; i < m; i++)
    {
        for (_clr(t), s[p = q = 0] = i; p <= q && match_1[i] < 0; p++)
        {
            for (k = s[p], j = 0; j < n && match_1[i] < 0; p++)
            {
                if (l_1[k] + l_2[j] == mat[k][j] && t[j] < 0)
                {
                    s[++q] = match_2[j], t[j] = k;
                    if (s[q] < 0)
                    {
                        for (p = j; p >= 0; j = p)
                        {
                            match_2[j] = k = t[j];
                            p = match_1[k];
                            match_1[k] = j;
                        }
                    }
                }
            }
        }
        if (match_1[i] < 0)
        {
            for (i--, p = inf, k = 0; k <= q; k++)
            {
                for (j = 0; j < n; j++)
                {
                    if (t[j] < 0 && l_1[s[k]] + l_2[j] - mat[s[k]][j] < p)
                    {
                        p = l_1[s[k]] + l_2[j] - mat[s[k]][j];
                    }
                }
            }
            for (j = 0; j < n; l_2[j] += t[j] < 0 ? 0 : p, j++);
            for (k = 0; k <= q; l_1[s[k++]] -= p);
        }
    }
    for (i = 0; i < m; i++)
    {   //  if处理无匹配的情况!!
        if (match_1[i] < 0)             //  ???
        {
            return -1;
        }
        if (mat[i][match_1[i]] <= -inf) //  ???
        {
            return -1;
        }
        ret += mat[i][match_1[i]];
    }
    return ret;
}

二分图多重匹配

const int MAXN = 1010;
const int MAXM = 510;
int uN, vN;
int g[MAXN][MAXM];
int linker[MAXM][MAXN];
bool used[MAXM];
int num[MAXM];  //  右边最大的匹配数

bool dfs(int u)
{
    for (int v = 0; v < vN; v++)
    {
        if (g[u][v] && !used[v])
        {
            used[v] = true;
            if (linker[v][0] < num[v])
            {
                linker[v][++linker[v][0]] = u;
                return true;
            }
            for (int i = 1; i <= num[0]; i++)
            {
                if (dfs(linker[v][i]))
                {
                    linker[v][i] = u;
                    return true;
                }
            }
        }
    }
    return false;
}

int hungary()
{
    int res = 0;
    for (int i = 0; i < vN; i++)
    {
        linker[i][0] = 0;
    }
    for (int u = 0; u < uN; u++)
    {
        memset(used, false, sizeof(used));
        if (dfs(u))
        {
            res++;
        }
    }
    return res;
}

无向图最小割

/*
 *  INIT: 初始化邻接矩阵g[][]
 *  CALL: res = mincut(n);
 *  注: Stoer-Wagner Minimum Cut;
 *  找边的最小集合,若其被删去则图变得不连通(我们把这种形式称为最小割问题)
 */
#define typec int               //  type of res
const typec inf = 0x3f3f3f3f;   //  max of res
const typec maxw = 1000;        //  maximum edge weight
const typec V = 10010;
typec g[V][V], w[V];
int a[V], v[V], na[V];

typec minCut(int n)
{
    int i, j, pv, zj;
    typec best = maxw * n * n;
    for (i = 0; i < n; i++)
    {
        v[i] = i;   //  vertex: 0 ~ n-1
    }
    while (n > 1)
    {
        for (a[v[0]] = 1, i = 1; i < n; i++)
        {
            a[v[i]] = 0;
            na[i - 1] = i;
            w[i] = g[v[0]][v[i]];
        }
        for (pv = v[0], i = 1; i < n; i++)
        {
            for (zj = -1, j = 1; j < n; j++)
            {
                if (!a[v[j]] && (zj < 0 || w[j] > w[zj]))
                {
                    zj = j;
                }
            }
            a[v[zj]] = 1;
            if (i == n - 1)
            {
                if (best > w[zj])
                {
                    best = w[zj];
                }
                for (i = 0; i < n; i++)
                {
                    g[v[i]][pv] = g[pv][v[i]] += g[v[zj]][v[i]];
                }
                v[zj] = v[--n];
                break;
            }
            pv = v[zj];
            for (j = 1; j < n; j++)
            {
                if(!a[v[j]])
                {
                    w[j] += g[v[zj]][v[j]];
                }
            }
        }
    }
    return best;
}

最大流

Dinic算法

/*
 *  Dinic 最大流 O(V^2 * E)
 *  INIT: ne=2; head[]置为0; addedge()加入所有弧; 
 *  CALL: flow(n, s, t);
 */
#define typec int               //  type of cost
const typec inf = 0x3f3f3f3f;   // max of cost
const typec E = 10010;
const typec N = 1010;

struct edge
{
    int x, y, nxt;
    typec c;
} bf[E];
int ne, head[N], cur[N], ps[N], dep[N];

void addedge(int x, int y, typec c)
{   //  add an arc(x->y, c);    vertex:0~n-1;
    bf[ne].x = x;
    bf[ne].y = y;
    bf[ne].c = c;
    bf[ne].nxt = head[x];
    head[x] = ne++;
    bf[ne].x = y;
    bf[ne].y = x;
    bf[ne].c = 0;
    bf[ne].nxt = head[y];
    head[y] = ne++;
    return ;
}

typec flow(int n, int s, int t)
{
    typec tr, res = 0;
    int i, j, k, f, r, top;
    while (1)
    {
        memset(dep, -1, n * sizeof(int));
        for (f = dep[ps[0] = s] = 0, r = 1; f != r;)
        {
            for (i = ps[f++], j = head[i]; j; j = bf[j].nxt)
            {
                if (bf[j].c && -1 == dep[k = bf[j].y])
                {
                    dep[k] = dep[i] + 1;
                    ps[r++] = k;
                    if (k == t)
                    {
                        f = r;
                        break;
                    }
                }
            }
        }
        if (-1 == dep[t])
        {
            break;
        }
        memcpy(cur, head, n * sizeof(int));
        for (i = s, top = 0; ;)
        {
            if (i == t)
            {
                for (k = 0, tr = inf; k < top; ++k)
                {
                    if (bf[ps[k]].c < tr)
                    {
                        tr = bf[ps[f = k]].c;
                    }
                }
                for (k = 0; k < top; ++k)
                {
                    bf[ps[k]].c -= tr, bf[ps[k]^1].c += tr;
                }
                res += tr;
                i = bf[ps[top = f]].x;
            }
            for (j = cur[i]; cur[i]; j = cur[i] = bf[cur[i]].nxt)
            {
                if (bf[j].c && dep[i] + 1 == dep[bf[j].y])
                {
                    break;
                }
            }
            if (cur[i])
            {
                ps[top++] = cur[i];
                i = bf[cur[i]].y;
            }
            else
            {
                if (0 == top)
                {
                    break;
                }
                dep[i] = -1;
                i = bf[ps[--top]].x;
            }
        }
    }
    return res;
}

HLPP算法

/*
 *  HLPP 最大流 O(V^3)
 *  INIT: network g; g.build(nv, ne); 
 *  CALL: res = g.maxflow(s, t);
 *  注意: 不要加入指向源点的边, 可能死循环.
 */
#define typef int               //  type of flow
const typef inf = 0x3f3f3f3f;   //  max of flow
const typef N = 10010;

typef minf(typef a, typef b)
{
    return a < b ? a : b;
}

struct edge
{
    int u, v;
    typef cuv, cvu, flow;
    edge (int x = 0, int y = 0, typef cu = 0, typef cv = 0, typef f = 0) : u(x), v(y), cuv(cu), cvu(cv), flow(f) {}
    int other(int p)
    {
        return p == u ? v : u;
    }
    typef cap(int p)
    {
        return p == u ? cuv - flow : cvu + flow;
    }
    void addflow(int p, typef f)
    {
        flow += (p == u ? f : -f);
        return ;
    }
};

struct vlist
{
    int lv, next[N], idx[2 * N], v;
    void clear(int cv)
    {
        v = cv;
        lv = -1;
        memset(idx, -1, sizeof(idx));
    }
    void insert(int n, int h)
    {
        next[n] = idx[h];
        idx[h] = n;
        if (lv < h)
        {
            lv = h;
        }
    }
    int remove()
    {
        int r = idx[lv];
        idx[lv] = next[idx[lv]];
        while (lv >= 0 && idx[lv] == -1)
        {
            lv--;
        }
        return r;
    }
    bool empty()
    {
        return lv < 0;
    }
};

struct network
{
    vector<edge>eg;
    vector<edge*>net[N];
    vlist list;
    typef e[N];
    int v, s, t, h[N], hn[2 * N], cur[N];
    void push(int);
    void relabel(int);
    void build(int, int);
    typef maxflow(int, int);
};

void network::push(int u)
{
    edge* te = net[u][cur[u]];
    typef ex = minf(te->cap(u), e[u]);
    int p = te->other(u);
    if (e[p] == 0 && p != t)
    {
        list.insert(p, h[p]);
    }
    te->addflow(u, ex);
    e[u] -= ex;
    e[p] += ex;
    return ;
}

void network::relabel(int u)
{
    int i, p, mh = 2 * v, oh = h[u];
    for (i = (int)net[u].size() - 1; i >= 0; i--)
    {
        p = net[u][i]->other(u);
        if (net[u][i]->cap(u) != 0 && mh > h[p] + 1)
        {
            mh = h[p] + 1;
        }
    }
    hn[h[u]]--;
    hn[mh]++;
    h[u] = mh;
    cur[u] = (int)net[u].size() - 1;
    if (hn[oh] != 0 || oh >= v + 1)
    {
        return ;
    }
    for (i = 0; i < v; i++)
    {
        if (h[i] > oh && h[i] <= v && i != s)
        {
            hn[h[i]]--;
            hn[v+1]++;
            h[i] = v + 1;
        }
    }
    return ;
}

typef network::maxflow(int ss, int tt)
{
    s = ss; t = tt;
    int i, p, u; typef ec;
    for (i = 0; i < v; i++)
    {
        net[i].clear();
    }
    for (i = (int)eg.size() - 1; i >= 0; i--)
    {
        net[eg[i].u].push_back(&eg[i]);
        net[eg[i].v].push_back(&eg[i]);
    }
    memset(h, 0, sizeof(h));
    memset(hn, 0, sizeof(hn));
    memset(e, 0, sizeof(e));
    e[s] = inf;
    for (i = 0; i < v; i++)
    {
        h[i] = v;
    }
    queue<int> q;
    q.push(t);
    h[t] = 0;
    while (!q.empty())
    {
        p = q.front();
        q.pop();
        for (i = (int)net[p].size() - 1; i >= 0; i--)
        {
            u = net[p][i]->other(p);
            ec = net[p][i]->cap(u);
            if (ec != 0 && h[u] == v && u != s)
            {
                h[u] = h[p] + 1;
                q.push(u);
            }
        }
    }
    for (i = 0; i < v; i++)
    {
        hn[h[i]]++;
    }
    for (i = 0; i < v; i++)
    {
        cur[i] = (int)net[i].size()-1;
    }
    list.clear(v);
    for (; cur[s] >= 0; cur[s]--)
    {
        push(s);
    }
    while (!list.empty())
    {
        for (u = list.remove(); e[u] > 0; )
        {
            if (cur[u] < 0)
            {
                relabel(u);
            }
            else if (net[u][cur[u]]->cap(u) > 0 && h[u] == h[net[u][cur[u]]->other(u)] + 1)
            {
                push(u);
            }
            else
            {
                cur[u]--;
            }
        }
    }
    return e[t];
}

void network::build(int n, int m)
{
    v = n;
    eg.clear();
    int a, b, i;
    typef l;
    for (i = 0; i < m; i++)
    {
        cin >> a >> b >> l;
        eg.push_back(edge(a, b, l, 0));     //  vertex: 0 ~ n-1
    }
    return ;
}

最小费用流

/*
 *  最小费用流 O(V * E * f)
 *  INIT: network g; g.build(v, e);
 *  CALL: g.mincost(s, t); flow=g.flow; cost=g.cost;
 *  注意: SPFA增广, 实际复杂度远远小于O(V * E);
 */
#define typef int               //  type of flow
#define typec int               //  type of dis
const typef inff = 0x3f3f3f3f;  //  max of flow
const typec infc = 0x3f3f3f3f;  //  max of dis
const int E = 10010;
const int N = 1010;

struct network
{
    int nv, ne, pnt[E], nxt[E];
    int vis[N], que[N], head[N], pv[N], pe[N];
    typef flow, cap[E];
    typec cost, dis[E], d[N];
    void addedge(int u, int v, typef c, typec w)
    {
        pnt[ne] = v;
        cap[ne] = c;
        dis[ne] = +w;
        nxt[ne] = head[u];
        head[u] = (ne++);
        pnt[ne] = u;
        cap[ne] = 0;
        dis[ne] = -w;
        nxt[ne] = head[v];
        head[v] = (ne++);
    }
    int mincost(int src, int sink)
    {
        int i, k, f, r;
        typef mxf;
        for (flow = 0, cost = 0; ;)
        {
            memset(pv, -1, sizeof(pv));
            memset(vis, 0, sizeof(vis));
            for (i = 0; i < nv; ++i)
            {
                d[i] = infc;
            }
            d[src] = 0;
            pv[src] = src;
            vis[src] = 1;
            for (f = 0, r = 1, que[0] = src; r != f;)
            {
                i = que[f++];
                vis[i] = 0;
                if (N == f)
                {
                    f = 0;
                }
                for (k = head[i]; k != -1; k = nxt[k])
                {
                    if(cap[k] && dis[k]+d[i] < d[pnt[k]])
                    {
                        d[pnt[k]] = dis[k] + d[i];
                        if (0 == vis[pnt[k]])
                        {
                            vis[pnt[k]] = 1;
                            que[r++] = pnt[k];
                            if (N == r)
                            {
                                r = 0;
                            }
                        }
                        pv[pnt[k]] = i;
                        pe[pnt[k]] = k;
                    }
                }
            }
            if (-1 == pv[sink])
            {
                break;
            }
            for (k = sink, mxf = inff; k != src; k = pv[k])
            {
                if (cap[pe[k]] < mxf)
                {
                    mxf = cap[pe[k]];
                }
            }
            flow += mxf;
            cost += d[sink] * mxf;
            for (k = sink; k != src; k = pv[k])
            {
                cap[pe[k]] -= mxf;
                cap[pe[k] ^ 1] += mxf;
            }
        }
        return cost;
    }

    void build(int v, int e)
    {
        nv = v;
        ne = 0;
        memset(head, -1, sizeof(head));
        int x, y;
        typef f;
        typec w;
        for (int i = 0; i < e; ++i)
        {
            cin >> x >> y >> f >> w;    //  vertex: 0 ~ n-1
            addedge(x, y, f, w);        //  add arc (u->v, f, w)
        }
    }
} g;


/*
 *  最小费用流 O(V^2 * f)
 *  INIT: network g; g.build(nv, ne);
 *  CALL: g.mincost(s, t); flow=g.flow; cost=g.cost;
 *  注意: 网络中弧的cost需为非负. 若存在负权, 进行如下转化: 
 *  首先如果原图有负环, 则不存在最小费用流. 那么可以用Johnson
 *  重标号技术把所有边变成正权,以后每次增广后进行维护,算法如下:
 *  1、用bellman-ford求s到各点的距离phi[];
 *  2、以后每求一次最短路,设s到各点的最短距离为dis[];
 *      for i = 1 to v do
 *          phi[v] += dis[v];
 *  下面的代码已经做了第二步,如果原图有负权,添加第一步即可。
 */
#define typef int               //  type of flow
#define typec int               //  type of cost
const typef inff = 0x3f3f3f3f;  //  max of flow
const typec infc = 0x3f3f3f3f;  //  max of cost
const int E = 10010;
const int N = 1010;

struct edge
{
    int u, v;
    typef cuv, cvu, flow;
    typec cost;
    edge (int x, int y, typef cu, typef cv, typec cc) :u(x), v(y), cuv(cu), cvu(cv), flow(0), cost(cc){}
    int other(int p)
    {
        return p == u ? v : u;
    }
    typef cap(int p)
    {
        return p == u ? cuv-flow : cvu+flow;
    }
    typec ecost(int p)
    {
        if (flow == 0)
        {
            return cost;
        }
        else if (flow > 0)
        {
            return p == u ? cost : -cost;
        }
        else
        {
            return p == u ? -cost : cost;
        }
    }
    void addFlow(int p, typef f)
    {
        flow += (p == u ? f : -f);
    }
};

struct network
{
    vector<edge> eg;
    vector<edge*> net[N];
    edge *prev[N];
    int v, s, t, pre[N], vis[N];
    typef flow;
    typec cost, dis[N], phi[N];
    bool dijkstra();
    void build(int nv, int ne);
    typec mincost(int, int);
};

bool network::dijkstra()
{
    //  使用O(E * logV)的Dij可降低整体复杂度至 O(E * logV * f)
    int i, j, p, u = 0;
    typec md, cw;
    for (i = 0; i < v; i++)
    {
        dis[i] = infc;
    }
    dis[s] = 0;
    prev[s] = 0;
    pre[s] = -1;
    memset(vis, 0, v * sizeof(int));
    for (i = 1; i < v; i++)
    {
        for (md = infc, j = 0; j < v; j++)
        {
            if (!vis[j] && md > dis[j])
            {
                md = dis[j];
                u = j;
            }
        }
        if (md == infc)
        {
            break;
        }
        for (vis[u] = 1, j = (int)net[u].size() - 1; j >= 0; j--)
        {
            edge *ce = net[u][j];
            if (ce->cap(u) > 0)
            {
                p = ce->other(u);
                cw = ce->ecost(u) + phi[u] - phi[p];
                //  !!  assert(cw >= 0);
                if (dis[p] > dis[u] + cw)
                {
                    dis[p] = dis[u] + cw;
                    prev[p] = ce;
                    pre[p] = u;
                }
            }
        }
    }
    return infc != dis[t];
}

typec network::mincost(int ss, int tt)
{
    s = ss;
    t = tt;
    int i, c;
    typef ex;
    flow = cost = 0;
    memset(phi, 0, sizeof(phi));
    //  !!  若原图含有负消费的边, 在此处运行Bellmanford
    //  将phi[i](0 <= i <= n - 1)置为mindist(s, i).
    for (i = 0; i < v; i++)
    {
        net[i].clear();
    }
    for (i = (int)eg.size() - 1; i >= 0; i--)
    {
        net[eg[i].u].push_back(&eg[i]);
        net[eg[i].v].push_back(&eg[i]);
    }
    while (dijkstra())
    {
        for (ex = inff, c = t; c != s; c = pre[c])
        {
            if (ex > prev[c]->cap(pre[c]))
            {
                ex = prev[c]->cap(pre[c]);
            }
        }
        for (c = t; c != s; c = pre[c])
        {
            prev[c]->addFlow(pre[c], ex);
        }
        flow += ex;
        cost += ex * (dis[t] + phi[t]);
        for (i = 0; i < v; i++)
        {
            phi[i] += dis[i];}
    }
    return cost;
}

void network::build(int nv, int ne)
{
    eg.clear();
    v = nv;
    int x, y;
    typef f;
    typec c;
    for (int i = 0; i < ne; ++i)
    {
        cin >> x >> y >> f >> c;
        eg.push_back(edge(x, y, f, 0, c));
    }
    return ;
}

有上下界的流

有上下界的最小(最大)流

/*
 *  有上下界的最小(最大)流
 *  INIT: up[][]为容量上界; low[][]为容量下界;
 *  CALL: mf = limitflow(n,src,sink); flow[][]为流量分配; 
 *  另附: 循环流问题
 *  描述: 无源无汇的网络N,设N是具有基础有向图D=(V,A)的网络.
 *       l和c分别为容量下界和容量上界. 如果定义在A上的函数 
 *       f满足: f(v, V) = f(V, v). V中任意顶点v,
 *       l(a)<=f(a)<=c(a),则称f为网络N的循环流.
 *  解法: 添加一个源s和汇t,对于每个下限容量l不为0的边(u, v),
 *       将其下限去掉,上限改为c-l,增加两条边(u, t),(s, v),
 *       容量均为l.原网络存在循环流等价于新网络最大流是满流.
 */
const int inf = 0x3f3f3f3f;
const int N = 1010;
int up[N][N], low[N][N], flow[N][N];
int pv[N], que[N], d[N];

void maxflow(int n, int src, int sink)
{
    //  BFS增广, O(E * maxflow)
    int p, q, t, i, j;
    do
    {
        for (i = 0; i < n; pv[i++] = 0);
        pv[t = src] = src + 1;
        d[t] = inf;
        for (p = q = 0; p <= q && !pv[sink]; t = que[p++])
        {
            for (i = 0; i < n; i++)
            {
                if (!pv[i] && up[t][i] && (j = up[t][i] - flow[t][i]) > 0)
                {
                    pv[que[q++] = i] = +t + 1, d[i] = d[t] < j ? d[t] : j;
                }
                else if (!pv[i] && up[i][t] && (j = flow[i][t]) > 0)
                {
                    pv[que[q++] = i] = -t - 1, d[i] = d[t] < j ? d[t] : j;
                }
            }
        }
        for (i = sink; pv[i] && i != src;)
        {
            if (pv[i] > 0)
            {
                flow[pv[i] - 1][i] += d[sink], i = pv[i] - 1;
            }
            else
            {
                flow[i][-pv[i] - 1] -= d[sink], i = -pv[i] - 1;
            }
        }
    }
    while (pv[sink]);
    return ;
}

int limitflow(int n, int src, int sink)
{
    int i, j, sk, ks;
    if (src == sink)
    {
        return inf;
    }
    up[n][n + 1] = up[n + 1][n] = up[n][n] = up[n + 1][n + 1] = 0;
    for (i = 0; i < n; i++)
    {
        up[n][i] = up[i][n] = up[n+1][i] = up[i][n+1] = 0;
        for (j = 0; j < n; j++)
        {
            up[i][j] -= low[i][j];
            up[n][i] += low[j][i];
            up[i][n + 1] += low[i][j];
        }
    }
    sk = up[src][sink];
    ks = up[sink][src];
    up[src][sink] = up[sink][src] = inf;
    maxflow(n + 2, n, n + 1);
    for (i = 0; i < n; i++)
    {
        if (flow[n][i] < up[n][i])
        {
            return -1;
        }
    }
    flow[src][sink] = flow[sink][src] = 0;
    up[src][sink] = sk;
    up[sink][src] = ks;     //  !min:src<-sink; max:src->sink;
    maxflow(n, sink, src);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            up[i][j] += low[i][j];
            flow[i][j] += low[i][j];
        }
    }
    for (j = i = 0; i < n; j += flow[src][i++]);
    return j;
}

最佳边割集

#define MAXN 100
#define inf 1000000000
int max_flow(int n, int mat[][MAXN], int source, int sink)
{
    int v[MAXN], c[MAXN], p[MAXN], ret = 0, i, j;
    for (;;)
    {
        for (i = 0; i < n; i++)
        {
            v[i] = c[i] = 0;
        }
        for (c[source] = inf; ;)
        {
            for (j = -1, i = 0; i < n; i++)
            {
                if (!v[i] && c[i] && (j == -1 || c[i] > c[j]))
                {
                    j = i;
                }
            }
            if (j < 0)
            {
                return ret;
            }
            if (j == sink)
            {
                break;
            }
            for (v[j] = 1, i = 0; i < n; i++)
            {
                if (mat[j][i] > c[i] && c[j] > c[i])
                {
                    c[i] = mat[j][i] < c[j] ? mat[j][i] : c[j], p[i] = j;
                }
            }
        }
        for (ret += j = c[i = sink]; i != source; i = p[i])
        {
            mat[p[i]][i] -= j, mat[i][p[i]] += j;
        }
    }
}

int best_edge_cut(int n, int mat[][MAXN], int source, int sink, int set[][2], int &mincost)
{
    int m0[MAXN][MAXN], m[MAXN][MAXN], i, j, k, l, ret = 0, last;
    if (source == sink)
    {
        return -1;
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            m0[i][j] = mat[i][j];
        }
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            m[i][j] = m0[i][j];
        }
    }
    mincost = last = max_flow(n, m, source, sink);
    for (k = 0; k < n && last; k++)
    {
        for (l = 0; l < n && last; l++)
        {
            if (m0[k][l])
            {
                for (i = 0; i < n + n; i++)
                {
                    for (j = 0; j < n + n; j++)
                    {
                        m[i][j] = m0[i][j];
                    }
                }
                m[k][l] = 0;
                if (max_flow(n, m, source, sink) == last - mat[k][l])
                {
                    set[ret][0] = k;
                    set[ret++][1] = l;
                    m0[k][l] = 0;
                    last -= mat[k][l];
                }
            }
        }
    }
    return ret;
}

最佳点割集

#define MAXN 100
#define inf 1000000000

int max_flow(int n, int mat[][MAXN], int source, int sink)
{
    int v[MAXN], c[MAXN], p[MAXN], ret = 0, i, j;
    for (;;)
    {
        for (i = 0; i < n; i++)
        {
            v[i] = c[i] = 0;
        }
        for (c[source] = inf; ;)
        {
            for (j = -1, i = 0; i < n; i++)
            {
                if (!v[i] && c[i] && (j == -1 || c[i] > c[j]))
                {
                    j = i;
                }
            }
            if (j < 0)
            {
                return ret;
            }
            if (j == sink)
            {
                break;
            }
            for (v[j] = 1, i = 0; i < n; i++)
            {
                if (mat[j][i] > c[i] && c[j] > c[i])
                {
                    c[i] = mat[j][i] < c[j] ? mat[j][i] : c[j], p[i] = j;
                }
            }
        }
        for (ret += j = c[i = sink]; i != source; i = p[i])
        {
            mat[p[i]][i] -= j, mat[i][p[i]] += j;
        }
    }
}

int best_vertex_cut(int n, int mat[][MAXN], int *cost, int source, int sink, int *set, int &mincost)
{
    int m0[MAXN][MAXN], m[MAXN][MAXN], i, j, k, ret = 0, last;
    if (source == sink || mat[source][sink])
    {
        return -1;
    }
    for (i = 0; i < n + n; i++)
    {
        for (j = 0; j < n + n; j++)
        {
            m0[i][j] = 0;
        }
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            if (mat[i][j])
            {
                m0[i][n + j] = inf;
            }
        }
    }
    for (i = 0; i < n; i++)
    {
        m0[n + i][i] = cost[i];
    }
    for (i = 0; i < n + n; i++)
    {
        for (j = 0; j < n + n; j++)
        {
            m[i][j] = m0[i][j];
        }
    }
    mincost = last = max_flow(n + n, m, source, n + sink);
    for (k = 0; k < n && last; k++)
    {
        if (k != source && k != sink)
        {
            for (i = 0; i < n + n; i++)
            {
                for (j = 0; j < n + n; j++)
                {
                    m[i][j] = m0[i][j];
                }
            }
            m[n + k][k] = 0;
            if (max_flow(n + n, m, source, n + sink) == last - cost[k])
            {
                set[ret++] = k;
                m0[n + k][k] = 0;
                last -= cost[k];
            }
        }
    }
    return ret;
}

最小边割集

#define MAXN 100
#define inf 1000000000

int max_flow(int n, int mat[][MAXN], int source, int sink)
{
    int v[MAXN], c[MAXN], p[MAXN], ret = 0, i, j;
    for (;;)
    {
        for (i = 0; i < n; i++)
        {
            v[i] = c[i] = 0;
        }
        for (c[source] = inf; ;)
        {
            for (j = -1, i = 0; i < n; i++)
            {
                if (!v[i] && c[i] && (j == -1 || c[i] > c[j]))
                {
                    j = i;
                }
            }
            if (j < 0)
            {
                return ret;
            }
            if (j == sink)
            {
                break;
            }
            for (v[j] = 1, i = 0; i < n; i++)
            {
                if (mat[j][i] > c[i] && c[j] > c[i])
                {
                    c[i] = mat[j][i] < c[j] ? mat[j][i] : c[j], p[i] = j;
                }
            }
        }
        for (ret += j = c[i = sink]; i != source; i = p[i])
        {
            mat[p[i]][i] -= j, mat[i][p[i]] += j;
        }
    }
}

int min_edge_cut(int n, int mat[][MAXN], int source, int sink, int set[][2])
{
    int m0[MAXN][MAXN], m[MAXN][MAXN], i, j, k, l, ret = 0, last;
    if (source == sink)
    {
        return -1;
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            m0[i][j] = (mat[i][j] != 0);
        }
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            m[i][j] = m0[i][j];
        }
    }
    last = max_flow(n, m, source, sink);
    for (k = 0; k < n && last; k++)
    {
        for (l = 0; l < n && last; l++)
        {
            if (m0[k][l])
            {
                for (i = 0; i < n + n; i++)
                {
                    for (j = 0; j < n + n; j++)
                    {
                        m[i][j] = m0[i][j];
                    }
                }
                m[k][l] = 0;
                if (max_flow(n, m, source, sink) < last)
                {
                    set[ret][0] = k;
                    set[ret++][1] = l;
                    m0[k][l] = 0;
                    last--;
                }
            }
        }
    }
    return ret;
}

最小点割集

/*
 *  最小点割集(点连通度)
 */
#define MAXN 100
#define inf 1000000000
int max_flow(int n, int mat[][MAXN], int source, int sink)
{
    int v[MAXN], c[MAXN], p[MAXN], ret = 0, i, j;
    for (; ;)
    {
        for (i = 0; i < n; i++)
        {
            v[i] = c[i] = 0;
        }
        for (c[source] = inf; ;)
        {
            for (j = -1, i = 0; i < n; i++)
            {
                if (!v[i] && c[i] && (j == -1 || c[i] > c[j]))
                {
                    j = i;
                }
            }
            if (j < 0)
            {
                return ret;
            }
            if (j == sink)
            {
                break;
            }
            for (v[j] = 1, i = 0; i < n; i++)
            {
                if (mat[j][i] > c[i] && c[j] > c[i])
                {
                    c[i] = mat[j][i] < c[j] ? mat[j][i] : c[j], p[i] = j;
                }
            }
        }
        for (ret += j = c[i = sink]; i != source; i = p[i])
        {
            mat[p[i]][i] -= j, mat[i][p[i]] += j;
        }
    }
}

int min_vertex_cut(int n, int mat[][MAXN], int source, int sink, int *set)
{
    int m0[MAXN][MAXN], m[MAXN][MAXN], i, j, k, ret = 0, last;
    if (source == sink || mat[source][sink])
    {
        return -1;
    }
    for (i = 0; i < n + n; i++)
    {
        for (j = 0; j < n + n; j++)
        {
            m0[i][j] = 0;
        }
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            if (mat[i][j])
            {
                m0[i][n + j] = inf;
            }
        }
    }
    for (i = 0; i < n; i++)
    {
        m0[n + i][i]=1;
    }
    for (i = 0; i < n + n; i++)
    {
        for (j = 0; j < n + n; j++)
        {
            m[i][j] = m0[i][j];
        }
    }
    last = max_flow(n + n, m, source, n + sink);
    for (k = 0; k < n && last; k++)
    {
        if (k != source && k != sink)
        {
            for (i = 0; i < n + n; i++)
            {
                for (j = 0; j < n + n; j++)
                {
                    m[i][j] = m0[i][j];
                }
            }
            m[n+k][k] = 0;
            if (max_flow(n + n, m, source, n + sink) < last)
            {
                set[ret++] = k;
                m0[n+k][k] = 0;
                last--;
            }
        }
    }
    return ret;
}

最小覆盖问题

最小路径覆盖

最小路径覆盖O(n^3)路径覆盖:就是在图中找一些路经,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联。 
最小路径覆盖:就是找出最少的路径条数,使之成为P的一个路径覆盖。 
路径覆盖与二分图匹配的关系:最小路径覆盖=|P|-最大匹配数;其中最大匹配数的求法是把P中的每个顶点pi分成两个顶点pi’与pi”,如果在p中存在一条pi到pj的边,那么在二分图P’中就有一条连接pi’与pj”的有向边(求二分图匹配时必须是单向边);这里pi’就是p中pi的出边,pj”就是p中pj的一条入边; 
有向图: 最小路径覆盖=|P|-最大匹配数; 
无向图: 最小路径覆盖=|P|-最大匹配数/2;

最小点集覆盖
结论:一个二分图中的最大匹配数等于这个图中的最小点覆盖数。
Structure 数据结构
划分树
/*
 *  划分树(查询区间第k大) 
 */
const int MAXN = 100010;

int tree[20][MAXN];     //  表示每层每个位置的值
int sorted[MAXN];       //  已经排序好的数
int toleft[20][MAXN];   //  toleft[p][i]表示第i层从1到i有数分入左边

void build(int l, int r, int dep)
{
    if (l == r)
    {
        return;
    }
    int mid = (l + r) >> 1;
    int same = mid - l + 1;         //  表示等于中间值而且被分入左边的个数
    for (int i = l; i <= r; i++)    //  注意是l,不是one
    {
        if (tree[dep][i] < sorted[mid])
        {
            same--;
        }
    }
    int lpos = l;
    int rpos = mid + 1;
    for (int i = l; i <= r; i++)
    {
        if (tree[dep][i] < sorted[mid])
        {
            tree[dep + 1][lpos++] = tree[dep][i];
        }
        else if (tree[dep][i] == sorted[mid] && same > 0)
        {
            tree[dep + 1][lpos++] = tree[dep][i];
            same--;
        }
        else
        {
            tree[dep + 1][rpos++] = tree[dep][i];
        }
        toleft[dep][i] = toleft[dep][l - 1] + lpos - l;
    }
    build(l, mid, dep + 1);
    build(mid + 1, r, dep + 1);
    return ;
}

//  查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int L, int R, int l, int r, int dep, int k)
{
    if(l == r)
    {
        return tree[dep][l];
    }
    int mid = (L + R) >> 1;
    int cnt = toleft[dep][r] - toleft[dep][l - 1];
    if (cnt >= k)
    {
        int newl = L + toleft[dep][l - 1] - toleft[dep][L - 1];
        int newr = newl + cnt - 1;
        return query(L, mid, newl, newr, dep + 1, k);
    }
    else
    {
        int newr = r + toleft[dep][R] - toleft[dep][r];
        int newl = newr - (r - l - cnt);
        return query(mid + 1, R, newl, newr, dep + 1, k - cnt);
    }
}

int main()
{
    int n, m;
    while (scanf("%d%d", &n, &m) == 2)
    {
        memset(tree, 0, sizeof(tree));
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &tree[0][i]);
            sorted[i] = tree[0][i];
        }
        sort(sorted + 1, sorted + n + 1);
        build(1, n, 0);
        int s, t, k;
        while(m--)
        {
            scanf("%d%d%d", &s, &t, &k);
            printf("%d\n", query(1, n, s, t, 0, k));
        }
    }
    return 0;
}

左偏树

/*
 *  合并复杂度 O(log N)
 *  INIT: init()读入数据并进行初始化;
 *  CALL: merge() 合并两棵左偏树; 
 *        ins() 插入一个新节点; 
 *        top() 取得最小结点; 
 *        pop() 取得并删除最小结点; 
 *        del() 删除某结点;
 *        add() 增/减一个结点的键值;
 *        iroot() 获取结点i的根;
 */
#define typec int       //  type of key val
const int na = -1;
const int N = 1010;

struct node
{
    typec key;
    int l, r, f, dist;
} tr[N];

int iroot(int i)
{   //  find i's root
    if (i == na)
    {
        return i;
    }
    while (tr[i].f != na)
    {
        i = tr[i].f;
    }
    return i;
}

int merge(int rx, int ry)
{
    //  two root:   rx, ry
    if (rx == na)
    {
        return ry;
    }
    if (ry == na)
    {
        return rx;
    }
    if (tr[rx].key > tr[ry].key)
    {
        swap(rx, ry);
    }
    int r = merge(tr[rx].r, ry);
    tr[rx].r = r;
    tr[r].f = rx;
    if (tr[r].dist > tr[tr[rx].l].dist)
    {
        swap(tr[rx].l, tr[rx].r);
    }
    if (tr[rx].r == na)
    {
        tr[rx].dist = 0;
    }
    else
    {
        tr[rx].dist = tr[tr[rx].r].dist + 1;
    }
    return rx;  //  return new root
}

int ins(int i, typec key, int root)
{   //  add a new node(i, key)
    tr[i].key = key;
    tr[i].l = tr[i].r = tr[i].f = na;
    tr[i].dist = 0;
    return root = merge(root, i);   //  return new root
}

int del(int i)
{   //  delete node i
    if (i == na)
    {
        return i;
    }
    int x, y, l, r;
    l = tr[i].l;
    r = tr[i].r;
    y = tr[i].f;
    tr[i].l = tr[i].r = tr[i].f = na;
    tr[x = merge(l, r)].f = y;
    if (y != na && tr[y].l == i)
    {
        tr[y].l = x;
    }
    if (y != na && tr[y].r == i)
    {
        tr[y].r = x;
    }
    for (; y != na; x = y, y = tr[y].f)
    {
        if (tr[tr[y].l].dist < tr[tr[y].r].dist)
        {
            swap(tr[y].l, tr[y].r);
        }
        if (tr[tr[y].r].dist + 1 == tr[y].dist)
        {
            break;
        }
        tr[y].dist = tr[tr[y].r].dist + 1;
    }
    if (x != na)    //  return new root
    {
        return iroot(x);
    }
    else return iroot(y);
}

node top(int root)
{
    return tr[root];
}

node pop(int &root)
{
    node out = tr[root];
    int l = tr[root].l, r = tr[root].r;
    tr[root].l = tr[root].r = tr[root].f = na;
    tr[l].f = tr[r].f = na;
    root = merge(l, r);
    return out;
}

int add(int i, typec val)   //  tr[i].key += val
{
    if (i == na)
    {
        return i;
    }
    if (tr[i].l == na && tr[i].r == na && tr[i].f == na)
    {
        tr[i].key += val;
        return i;
    }
    typec key = tr[i].key + val;
    int rt = del(i);
    return ins(i, key, rt);
}

void init(int n)
{
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &tr[i].key);    //  %d: type of key
        tr[i].l = tr[i].r = tr[i].f = na;
        tr[i].dist = 0;
    }
    return ;
}

线段树

求矩形并的面积(线段树+离散化+扫描线)

Each test case starts with a line containing a single integer n (1 <= n <= 100) of available maps. 
The n following lines describe one map each. 
Each of these lines contains four numbers x1、y1、x2、y2 (0 <= x1 < x2 <= 100000; 0 <= y1 < y2 <= 100000), not necessarily integers. 
The values (x1, y1) and (x2,y2) are the coordinates of the 
top-left resp. 
Bottom-Right corner of the mapped area.
/*
 *  本题中的坐标是浮点类型的, 故不能将坐标直接离散.我们必须为它们建立一个对应关系,
 *  用一个整数去对应一个浮点数这样的对应关系在本题的数组y[]中。
 */
struct node
{
    int st, ed, c;      //  c: 区间被覆盖的层数, m: 区间的测度
    double m;
} ST[802];

struct line
{
    double x, y1, y2;   //  纵方向直线, x:直线横坐标, y1 y2:直线上的下面与上面的两个纵坐标
    bool s;             //  s = 1 : 直线为矩形的左边, s = 0:直线为矩形的右边
} Line[205];

double y[205], ty[205]; //  y[]整数与浮点数的对应数组; ty[]:用来求y[]的辅助数组

void build(int root, int st, int ed)
{
    ST[root].st = st;
    ST[root].ed = ed;
    ST[root].c = 0;
    ST[root].m = 0;
    if (ed - st > 1)
    {
        int mid = (st + ed) / 2;
        build(root * 2, st, mid);
        build(root * 2 + 1, mid, ed);
    }
    return ;
}

void updata(int root)
{
    if (ST[root].c > 0)
    {   //  将线段树上区间的端点分别映射到y[]数组所对应的浮点数上,由此计算出测度
        ST[root].m = y[ST[root].ed - 1] - y[ST[root].st - 1];
    }
    else if (ST[root].ed - ST[root].st == 1)
    {
        ST[root].m = 0;
    }
    else
    {
        ST[root].m = ST[root * 2].m + ST[root * 2 + 1].m;
    }
    return ;
}

void insert(int root, int st, int ed)
{
    if (st <= ST[root].st && ST[root].ed <= ed)
    {
        ST[root].c++;
        updata(root);
        return ;
    }
    if (ST[root].ed - ST[root].st == 1)
    {
        return ;    //  不出错的话 这句话就是冗余的
    }
    int mid = (ST[root].ed + ST[root].st) / 2;
    if (st < mid)
    {
        insert(root * 2, st, ed);
    }
    if (ed > mid)
    {
        insert(root * 2 + 1, st, ed);
    }
    updata(root);
    return ;
}

void Delete(int root, int st, int ed)
{
    if (st <= ST[root].st && ST[root].ed <= ed)
    {
        ST[root].c--;
        updata(root);
        return ;
    }
    if (ST[root].ed - ST[root].st == 1)
    {
        return ;    //  不出错的话 这句话就是冗余的
    }
    int mid = (ST[root].st + ST[root].ed) / 2;
    if (st < mid)
    {
        Delete(root * 2, st, ed);
    }
    if (ed > mid)
    {
        Delete(root * 2 + 1, st, ed);
    }
    updata(root);
    return ;
}

int Correspond(int n, double t)
{
    //  二分查找出浮点数t在数组y[]中的位置(此即所谓的映射关系)
    int low, high, mid;
    low = 0;
    high = n - 1;
    while (low < high)
    {
        mid = (low + high) / 2;
        if (t > y[mid])
        {
            low = mid + 1;
        }
        else
        {
            high = mid;
        }
    }
    return high + 1;
}

bool cmp(line l1, line l2)
{
    return l1.x < l2.x;
}

int main()
{
    int n, i, num, l, r, c = 0;
    double area, x1, x2, y1, y2;
    while (cin >> n, n)
    {
        for (i = 0; i < n; i++)
        {
            cin >> x1 >> y1 >> x2 >> y2;
            Line[2 * i].x = x1;
            Line[2 * i].y1 = y1;
            Line[2 * i].y2 = y2;
            Line[2 * i].s = 1;
            Line[2 * i + 1].x = x2;
            Line[2 * i + 1].y1 = y1;
            Line[2 * i + 1].y2 = y2;
            Line[2 * i + 1].s = 0;
            ty[2 * i] = y1;
            ty[2 * i + 1] = y2;
        }
        n <<= 1;
        sort(Line, Line + n, cmp);
        sort(ty, ty + n);
        y[0] = ty[0];
        //  处理数组ty[]使之不含重覆元素,得到新的数组存放到数组y[]中
        for (i = num = 1; i < n; i++)
        {
            if (ty[i] != ty[i - 1])
            {
                y[num++] = ty[i];
            }
        }
        build(1, 1, num);   //  树的叶子节点与数组y[]中的元素个数相同,以便建立一一对应的关系
        area = 0;
        for (i = 0; i < n - 1; i++)
        {   //  由对应关系计算出线段两端在树中的位置
            l = Correspond(num, Line[i].y1);
            r = Correspond(num, Line[i].y2);
            if (Line[i].s)  //  插入矩形的左边
            {
                insert(1, l, r);
            }
            else            //  删除矩形的右边
            {
                Delete(1, l, r);
            }
            area += ST[1].m * (Line[i + 1].x - Line[i].x);
        }
        cout << "Test case #" << ++c << endl << "Total explored area: ";
        cout << fixed << setprecision(2) << area << endl << endl;   //  需要引入iomanip头文件
    }
    return 0;
}

求矩形并的周长(线段树+离散化+扫描线)

The first line contains the number of rectangles pasted on the wall. 
In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. 
The values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate. 0 <= number of rectangles < 5000. 
All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.
struct node
{
    int st, ed, m, lbd, rbd;
    int sequence_line, count;
} ST[40005];

void build(int st, int ed, int v)       //  建树,区间为[st, ed]
{
    ST[v].st = st;
    ST[v].ed = ed;
    ST[v].m = ST[v].lbd = ST[v].rbd = 0;
    ST[v].sequence_line = ST[v].count = 0;
    if (ed - st > 1)
    {
        int mid = (st + ed) / 2;
        build(st, mid, 2 * v + 1);
        build(mid, ed, 2 * v + 2);
    }
    return ;
}

void UpData(int v)                      //  更新结点区间的测度
{
    if (ST[v].count > 0)
    {
        ST[v].m = ST[v].ed - ST[v].st;
        ST[v].lbd = ST[v].rbd = 1;
        ST[v].sequence_line = 1;
        return ;
    }
    if (ST[v].ed - ST[v].st == 1)
    {
        ST[v].m = 0;
        ST[v].lbd = ST[v].rbd = 0;
        ST[v].sequence_line = 0;
    }
    else
    {
        int left = 2 * v + 1, right = 2 * v + 2;
        ST[v].m = ST[left].m + ST[right].m;
        ST[v].sequence_line = ST[left].sequence_line + ST[right].sequence_line - (ST[left].rbd & ST[right].lbd);
        ST[v].lbd = ST[left].lbd;
        ST[v].rbd = ST[right].rbd;
    }
    return ;
}

void insert(int st, int ed, int v)
{
    if (st <= ST[v].st && ed >= ST[v].ed)
    {
        ST[v].count++;
        UpData(v);
        return ;
    }
    int mid = (ST[v].st + ST[v].ed) / 2;
    if (st < mid)
    {
        insert(st, ed, 2 * v + 1);
    }
    if (ed > mid)
    {
        insert(st, ed, 2 * v + 2);
    }
    UpData(v);
    return ;
}

void Delete(int st, int ed, int v)
{
    if (st <= ST[v].st && ed >= ST[v].ed)
    {
        ST[v].count--;
        UpData(v);
        return ;
    }
    int mid = (ST[v].st + ST[v].ed) / 2;
    if (st < mid)
    {
        Delete(st, ed, 2 * v + 1);
    }
    if (ed > mid)
    {
        Delete(st, ed, 2 * v + 2);
    }
    UpData(v);
    return ;
}

struct line
{
    int x, y1, y2;  //  y1 < y2
    bool d;         //  d=true表示该线段为矩形左边,d=false表示该线段为矩形的右边
} a[10003];

bool cmp(line t1, line t2)  //  为线段排序的函数,方便从左向右的扫描
{
    return t1.x < t2.x;
}

void cal_C(int n);

int main()
{
    int n, x1, x2, y1, y2, i, j, suby, upy;
    while (scanf("%d",&n) != EOF)
    {
        j = 0;
        suby = 10000;
        upy = -10000;
        for (i = 0; i < n; i++)
        {
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            a[j].x = x1;
            a[j].y1 = y1;
            a[j].y2 = y2;
            a[j].d = 1;
            j++;
            a[j].x = x2;
            a[j].y1 = y1;
            a[j].y2 = y2;
            a[j].d = 0;
            j++;
            if (suby > y1)
            {
                suby = y1;
            }
            if (upy < y2)
            {
                upy = y2;
            }
        }
        sort(a, a + j, cmp);
        build(suby, upy, 0);
        cal_C(j);
    }
    return 0;
}

void cal_C(int n)
{
    int i, t2, sum = 0;
    t2 = 0;
    a[n] = a[n - 1];
    for (i = 0; i < n; i++)
    {
        if (a[i].d == 1)
        {
            insert(a[i].y1, a[i].y2, 0);
        }
        else
        {
            Delete(a[i].y1, a[i].y2, 0);
        }
        sum += ST[0].sequence_line * (a[i + 1].x - a[i].x) * 2;
        sum += abs(ST[0].m - t2);
        t2 = ST[0].m;
    }
    printf("%d\n", sum);
}

主席树

查找区间有多少个不同的数
/*
 *  给出一个序列,查询区间内有多少个不相同的数 
 */
const int MAXN = 30010;
const int M = MAXN * 100;
int n, q, tot;
int a[MAXN];
int T[MAXN], lson[M], rson[M], c[M];

int build(int l, int r)
{
    int root = tot++;
    c[root] = 0;
    if (l != r)
    {
        int mid = (l + r) >> 1;
        lson[root] = build(l, mid);
        rson[root] = build(mid + 1, r);
    }
    return root;
}

int update(int root, int pos, int val)
{
    int newroot = tot++, tmp = newroot;
发布了217 篇原创文章 · 获赞 125 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39885372/article/details/104205324