kuangbin带你飞 匹配问题 二分匹配 + 二分图多重匹配 + 二分图最大权匹配 + 一般图匹配带花树

二分匹配:二分图的一些性质

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

1。一个二分图中的最大匹配数等于这个图中的最小点覆盖数

König定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数。如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖所有的边。

2。最小路径覆盖=最小路径覆盖=|G|-最大匹配数  在一个N*N的有向图中,路径覆盖就是在图中找一些路经,使之覆盖了图中的所有顶点,  且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,  那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每每条路径就是一个弱连通子集.

由上面可以得出:

1.一个单独的顶点是一条路径;  2.如果存在一路径p1,p2,......pk,其中p1 为起点,pk为终点,那么在覆盖图中,顶点p1,p2,......pk不再与其它的    顶点之间存在有向边.

最小路径覆盖就是找出最小的路径条数,使之成为G的一个路径覆盖.

路径覆盖与二分图匹配的关系:最小路径覆盖=|G|-最大匹配数;

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

独立集:图中任意两个顶点都不相连的顶点集合。

二分图判定:染色判定法

直接利用题目HDU 2444 The Accomodation of Students

题意:给定n个学生,他们之间可能互相认识,首先判断能不能将这些学生分为两组,使组内学生不认识;

现想将学生两两分组,且保证每一组的学生都认识,这样分组可达到的最大组数为多大?

二分图判定+二分图最大匹配 直接看代码

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 410;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

bool used[MAXN];
int linker[MAXN],N,M;
int color[MAXN];

bool bipartite(int u)
{
    for (int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].v;
        if (color[v] == color[u]) return false;
        if (!color[v])
        {
            color[v] = 3 - color[u];
            if (!bipartite(v)) return false;
        }
    }
    return true;
}

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

int calcu()
{
    memset(linker,-1,sizeof(linker));
    int ret = 0;
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}
int main()
{
    while (scanf("%d%d",&N,&M) != EOF)
    {
        init();
        memset(color,0,sizeof(color));
        for (int i = 0 ; i < M ; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v,9797);
           // add_edge(v,u,9797);
        }
        bool flag = false;
        for (int i = 1 ; i <= N ; i++)
        {
            if (color[i] == 0)
            {
                color[i] = 1;
                if (!bipartite(i))
                {
                    flag = true;
                    break;
                }
            }
        }
        if (!flag)
        {
            printf("%d\n",calcu());
        }
        else puts("No");
    }
    return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————

2份模板

1:匈牙利算法 点编号从1开始到N 复杂度O(VE)

const int MAXN = 110;
const int MAXM = 100010;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0 ;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

第二份 Hopcroft_Krap 复杂度O(根号V*E) 这份模板从0点编号

const int MAXN = 3010;
const int MAXM = 3000010;
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 = 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 = 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 ret = 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)) ret++;
    }
    return ret;
}

用第一道题来表示这2个模板怎么用的

HDU 1045 Fire Net

题意:在有墙的图中放尽量多的车满足相互不可攻击,球最多放多少个车;

建图:因为有墙的原因不可按照经典二分图简单建图,可以重新最每个点所在的行列进行编号。如果遇到墙或者到达边界编号更新。具体看代码

2分代码来显示模板用法 1:匈牙利

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 110;
const int MAXM = 100010;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0 ;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

char G[10][10];
int idrow[10][10],idcol[10][10];
int main()
{
   // freopen("sample.txt","r",stdin);
    while (scanf("%d",&n) != EOF)
    {
        if (n == 0) break;
        for (int i = 1 ; i <= n ; i++) scanf("%s",G[i] + 1);
        init();
        int idx = 1,idy = 1;
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                if (G[i][j] == ‘X‘) idx++;
                else idrow[i][j] = idx;
                if (j == n) idx++;
               // printf("%d ",idrow[i][j]);
            }
        }
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                if (G[j][i] == ‘X‘) idy++;
                else idcol[j][i] = idy;
                if(j == n) idy++;
                //printf("%d ",idcol[i][j]);
            }
        }
       // printf("%d %d\n",idx,idy);
        N = idx - 1;
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                if (G[i][j] == ‘.‘)
                    add_edge(idrow[i][j],idcol[i][j],9797);
            }
        }
        printf("%d\n",calcu());
    }
    return 0;
}

2:HK

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 3010;
const int MAXM = 3000010;
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 = 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 = 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 ret = 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)) ret++;
    }
    return ret;
}

char g[10][10];
int n;
int idrow[10][10],idcol[10][10];
int main()
{
    //freopen("sample.txt","r",stdin);
    while (scanf("%d",&n) != EOF)
    {
        if (n == 0) break;
        for (int i = 0 ; i <= n * n ; i++)G[i].clear();
        for (int i = 1 ; i <= n ; i++) scanf("%s",g[i] + 1);
        int idx = 0,idy = 0;
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                if (g[i][j] == ‘X‘) idx++;
                else idrow[i][j] = idx;
                if (j == n) idx++;
               // printf("%d ",idrow[i][j]);
            }
        }
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                if (g[j][i] == ‘X‘) idy++;
                else idcol[j][i] = idy;
                if(j == n) idy++;
                //printf("%d ",idcol[i][j]);
            }
        }
       // printf("%d %d\n",idx,idy);
       // N = idx - 1;
        uN = idx;
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                if (g[i][j] == ‘.‘) G[idrow[i][j]].push_back(idcol[i][j]);
                   // add_edge(idrow[i][j],idcol[i][j],9797);
            }
        }
        printf("%d\n",Maxmatch());
    }
    return 0;
}

HDU 1083 Courses

题意就是选个课代表。很简单直接做

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 310;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= P ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&P,&N);
        init();
        for (int i = 1 ; i <= P ; i++)
        {
            int cnt;
            scanf("%d",&cnt);
            while(cnt--)
            {
                int x;
                scanf("%d",&x);
                add_edge(i,x,9797);
            }
        }
        printf("%s\n",calcu() >= P ? "YES" : "NO");
    }
    return 0;
}

HDU 1281 棋盘游戏

中文题目

一开始觉得这题好难。没想到直接爆就行了

先求一边最大匹配,然后根据标记的linker数组吧对应匹配删掉,同时删掉这条边,然后重新跑匈牙利看是不是最大匹配变化即可

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 210;
const int INF = 0x3f3f3f3f;
const int MAXM = MAXN * MAXN;
int N,M,K;
int G[MAXN][MAXN];

void init()
{
    memset(G,0,sizeof(G));
}

bool used[MAXN];
int linker[MAXN],rlinker[MAXN];
int important;

bool dfs(int u,bool first)
{
    for (int i = 1 ; i <= N ; i++)
    {
        if (!G[u][i]) continue;
        int v = i;
        if (!used[v])
        {
            used[v] = true;
            if (linker[v] == -1 || dfs(linker[v],first))
            {
                if (first)
                {
                    linker[v] = u;
                    rlinker[u] = v;
                }
                return true;
            }
        }
    }
    return false;
}

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    memset(rlinker,-1,sizeof(rlinker));
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i,true)) ret++;
    }
    return ret;
}

bool judge()
{
    for (int i = 1;  i <= N ; i++)
    {
        if (rlinker[i] != -1) continue;
        memset(used,false,sizeof(used));
        if (dfs(i,false)) return false;
    }
    return true;
}

int main()
{
    //freopen("sample.txt","r",stdin);
    int kase = 1;
    while (scanf("%d%d%d",&N,&M,&K) != EOF)
    {
        init();
        for (int i = 0 ; i < K ; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v] = true;
        }
        int num = calcu();
        important = 0;
        for (int u = 1 ; u <= N ; u++)
        {
            if (rlinker[u] == -1) continue;
            int v = rlinker[u];
            G[u][v] = false;
            linker[v] = -1;
            rlinker[u] = -1;
            if (judge()) important++;
            G[u][v] = true;
            linker[v] = u;
            rlinker[u] = v;
        }
        printf("Board %d have %d important blanks for %d chessmen.\n",kase++,important,num);
    }
    return 0;
}

HDU 2819 Swap
交换某两行或者某两列使得主对角线全都是1

首先交换行和列是等价的。

建图:如果第i行的第j个数字为1,那么i和j连一条边,表示如果交换第i行第j行那么矩阵中A[j][j] = 1;

建图之后跑最大匹配,如果最大匹配小于行数,那么不可行

否则 就按照上面建图的方式输出答案,注意上面的建图是基于行列一开始的样子,输出是要一步一步的,每一步交换都会影响后边的。这里根据

linker数组处理就好

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 210;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;
int N;

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

void add_edge(int u,int v ,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}
vector<pair<int,int> >ans;

int main()
{
    //freopen("sample.txt","r",stdin);
    while (scanf("%d",&N) != EOF)
    {
        init();
        ans.clear();
        for (int i = 1 ; i <= N ; i++)
        {
            for (int j = 1; j <= N; j++)
            {
                int x;
                scanf("%d",&x);
                if(x) add_edge(i,j,9797);
            }
        }
        int ret = calcu();
        if (ret < N) puts("-1");
        else
        {
            for (int i = 1 ; i <= N ; i++)
            {
                int j;
                for (j = 1 ; j <= N;  j++)
                    if (linker[j] == i) break;
                if (i != j)
                {
                    ans.push_back(make_pair(i,j));
                    swap(linker[i],linker[j]);
                }
            }
            printf("%d\n",ans.size());
            for (int i = 0 ; i < (int)ans.size() ; i++)
            {
                printf("C %d %d\n",ans[i].first,ans[i].second);
            }
        }
    }
    return 0;
}

HDU 2389 Rain on your Parade
这里必须要用HK求。裸题

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 3010;
const int MAXM = 3000010;
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 = 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 = 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 ret = 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)) ret++;
    }
    return ret;
}

int Time,N,M;
int X[MAXN],Y[MAXN],v[MAXN];
int umbx[MAXN],umby[MAXN];

int main()
{
    int T,kase = 1;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&Time,&M);
        for (int i = 0 ; i < M ; i++)
            scanf("%d%d%d",&X[i],&Y[i],&v[i]);
        for (int i = 0 ; i < M ; i++)G[i].clear();
        uN = M;
        scanf("%d",&N);
        for (int i = 0 ; i < N ; i++)
            scanf("%d%d",&umbx[i],&umby[i]);
        for (int i = 0 ; i < M ; i++)
        {
            for (int j = 0 ; j < N ; j++)
            {
                double dist = sqrt((umbx[j] - X[i]) * (umbx[j] - X[i])
                        + (umby[j] - Y[i]) * (umby[j] - Y[i]));
               // printf("%d %d %lf\n",i,j,dist);
                if (1.0 * v[i] * Time >= dist)
                    G[i].push_back(j);
            }
        }
        printf("Scenario #%d:\n",kase++);
        printf("%d\n\n",Maxmatch());
    }
    return 0;
}

HDU 4185 Oil Skimming
最多有多少个相邻的2个#。

每个#号编号,相邻则连边跑最大匹配,最后答案除以2

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 610;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
const int dx[] = {0,0,-1,1};
const int dy[] = {1,-1,0,0};
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;
int n;
int N,M;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

char G[MAXN][MAXN];
int id[MAXN][MAXN];
int main()
{
    int T,kase = 1;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        memset(id,0,sizeof(id));
        int idx = 1;
        for (int i = 1 ; i <= n ; i++)
            scanf("%s",G[i] + 1);
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                if (G[i][j] == ‘#‘) id[i][j] = idx++;
            }
        }
        N = idx - 1;
        init();
        for (int i = 1 ; i <= n ; i++)
        {
            for (int j = 1 ; j <= n ; j++)
            {
                for (int d = 0 ; d < 4 ; d++)
                {
                    int nx = i + dx[d];
                    int ny = j + dy[d];
                    if (nx >= 1 && nx <= n && ny >= 1 && ny <= n && G[nx][ny] == ‘#‘)
                    {
                        add_edge(id[i][j],id[nx][ny],9797);
                    }
                }
            }
        }
        printf("Case %d: %d\n",kase++,calcu() / 2);
    }
    return 0;
}

POJ 3020 Antenna Placement

跟上题类似。注意答案为什么是总数-匹配数/2

首先一次匹配连接2个点。那么含义就是有2个点连在一起的边有这么多个。剩下的一个点占用2个的资源。说的不清楚看下面的式子

实际上的式子为点总数 - (最大匹配)/2 * 2 + (最大匹配 / 2);

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 410;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
const int dx[] = {0,0,-1,1};
const int dy[] = {1,-1,0,0};
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;
int un,vn;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= un ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret++;
}

int N,M;
int id[50][50];
char G[50][50];

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&N,&M);
        for (int i = 1 ; i <= N ; i++) scanf("%s",G[i] + 1);
        int leap = 0;
        for (int i = 1 ; i <= N ; i++)
        {
            for (int j = 1;  j <= M ; j++)
            {
                if (G[i][j] == ‘*‘) id[i][j] = ++leap;
            }
        }
        init();
        un = leap;
        for (int i = 1 ; i <= N ; i++)
        {
            for (int j = 1 ; j <= M ; j++)
            {
                if (G[i][j] != ‘*‘) continue;
                for (int d = 0 ; d < 4 ; d++)
                {
                    int nx = i + dx[d];
                    int ny = j + dy[d];
                    if (nx >= 1 && nx <= N && ny >= 1 && ny <= M && G[nx][ny] == ‘*‘)
                    {
                        add_edge(id[i][j],id[nx][ny],9797);
                    }
                }
            }
        }
        printf("%d\n",leap - calcu() / 2);
    }
    return 0;
}

HDU 1054 Strategic Game
求最小点覆盖

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 1510;
const int MAXM = 200010;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;
int un,vn;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 0;  i <= un ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

int N;

int main()
{
    while (scanf("%d",&N) != EOF)
    {
        init();
        un = N;
        for (int i = 1 ; i <= N ; i++)
        {
            int u,cnt;
            scanf("%d:(%d)",&u,&cnt);
            while (cnt--)
            {
                int x;
                scanf("%d",&x);
                add_edge(u,x,9797);
                add_edge(x,u,9797);
              //  printf("%d %d\n",u,x);
            }
        }
        printf("%d\n",calcu() / 2);
    }
    return 0;
}

还有树形DP解法

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 1510;
const int MAXM = 150010;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

int N;
int dp[MAXN][2];

int dfs(int u,int sta,int fa)
{
    if (dp[u][sta] < INF) return dp[u][sta];
    dp[u][sta] = sta;
    for (int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].v;
        if (v == fa) continue;
        if (sta == 0)
            dp[u][sta] += dfs(v,1,u);
        else dp[u][sta] += min(dfs(v,1,u),dfs(v,0,u));
    }
    return dp[u][sta];
}

int main()
{
    while (scanf("%d",&N) != EOF)
    {
        init();
        int root = 1;
        for (int i = 1 ; i <= N ; i++)
        {
            int u,cnt;
            scanf("%d:(%d)",&u,&cnt);
            if (cnt > 1) root = u;
            while (cnt--)
            {
                int x;
                scanf("%d",&x);
                add_edge(u,x,9797);
                add_edge(x,u,9797);
              //  printf("%d %d\n",u,x);
            }
        }
        memset(dp,0x3f,sizeof(dp));
        int ans = min(dfs(root,0,-1),dfs(root,1,-1));
        printf("%d\n",ans);
    }
    return 0;
}

HDU 1151 Air Raid

一个=城镇中有n个路口和m条单项的路径,图是无环图,现在要派一些伞兵去这些成寿寺,要到达所有的路口;

有一些或者没有伞兵可以不去那些路口,只要其他人能完成这么任务;

每个在一个路口着陆了的伞兵可以沿着街去到其他路口;

我们的任务就是求出去执行任务的伞兵最少可以使几个;

二分图最小路径覆盖

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 250;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;
int N,M;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&N,&M);
        init();
        while (M--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v,9797);
        }
        printf("%d\n",N - calcu());
    }
    return 0;
}

POJ 2594 Treasure Exploration

每个点可以走多次的最小路径覆盖

用floyd预处理,然后一样做就行

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 510;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
int N,M;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 1 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

int G[MAXN][MAXN];
int main()
{
    //freopen("sample.txt","r",stdin);
    while (scanf("%d%d",&N,&M) != EOF)
    {
        if (N == 0 && M == 0) break;
        memset(G,0x3f,sizeof(G));
        for (int i = 1;  i <= M ; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v] = 1;
        }
        for (int k = 1; k <= N ; k++)
        {
            for (int i = 1 ; i <= N ; i++)
            {
                for (int j = 1 ; j <= N ; j++)
                    G[i][j] = min(G[i][j],G[i][k] + G[k][j]);
            }
        }
        init();
        for (int i = 1 ; i <= N ; i++)
        {
            for (int j = 1 ; j <= N ; j++)
            {
                if (G[i][j] < INF)
                    add_edge(i,j,9797);
            }
        }
        printf("%d\n",N - calcu());
    }
    return 0;
}

HDU 3829 Cat VS Dog

建图方法:不管猫狗怎么样,如果2个小孩是矛盾的那么连一条边;然后求最大独立集

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 510;
const int MAXM = MAXN * MAXN * 2;
const int INF = 0x3f3f3f3f;
int N,M;
struct Edge
{
    int u,v,next;
    int w;
}edge[MAXM];
int head[MAXN],tot;

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

void add_edge(int u,int v,int w)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

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

int calcu()
{
    int ret = 0;
    memset(linker,-1,sizeof(linker));
    for (int i = 0 ; i <= N ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}
int cat,dog;
string like[MAXN],dislike[MAXN];

int main()
{
    while (scanf("%d%d%d",&cat,&dog,&N) != EOF)
    {
        init();
        for (int i = 0 ; i < N ; i++)
        {
            cin >> like[i] >> dislike[i];
        }
        for (int i = 0 ; i < N ; i++)
        {
            for (int j = 0 ; j < N ; j++)
            {
                if (i == j) continue;
                if (like[i] == dislike[j] || dislike[i] == like[j])
                    add_edge(i,j,9797);
            }
        }
        printf("%d\n",N - calcu() / 2);
    }
    return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————

二分图多重匹配

Y部中的一个点可以连接X部的多个点。但是有连点的相关限制,求最大匹配数目的问题

二分图的多重匹配算法的实现类似于匈牙利算法,对于集合C中的元素xi,找到一个与其相连的元素yi后,检查匈牙利算法的两个条件是否成立,若yi未被匹配,则将
xi,yi匹配。否则,如果与yi匹配的元素已经达到上限,那么在所有与yi匹配的元素中选择一个元素,检查是否能找到一条增广路径,如果能,则让出位置,让xi与yi匹配。

模板

const int MAXN = 310;
const int INF = 0x3f3f3f3f;
bool G[MAXN][MAXN];
int dis[MAXN][MAXN];
bool used[MAXN];
int linker[MAXN][MAXN];
int num[MAXN];//Y部每个可以连接的个数,即容量
int NX,NY;
int K,C,M;

bool dfs(int u)
{
    for (int i = 1 ; i <= NY ; i++)
    {
        if(G[u][i] && !used[i])
        {
            used[i] = true;
            if (linker[i][0] < num[i])
            {
                linker[i][++linker[i][0]] = u;
                return true;
            }
            else
            {
                for (int j = 1 ; j <= num[i] ; j++)
                {
                    if (dfs(linker[i][j]))
                    {
                        linker[i][j] = u;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

int calcu(int mid)
{
    for (int i = 0 ; i <= NY ; i++) linker[i][0] = 0;
    int ret = 0;
    for (int i = 1 ; i <= NX ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    return ret;
}

POJ 2289 Jamie‘s Contact Groups

给出一个图,然后让你分组,每组的个数尽量少

二分

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 2010;
bool G[MAXN][MAXN];
bool used[MAXN];
int NX,NY;
char str[MAXN];
int cnt[MAXN];
int linker[MAXN][MAXN];

bool dfs(int u,int mid)
{
    for (int i = 0 ; i < NY ; i++)
    {
        if (G[u][i] && !used[i])
        {
            used[i] = true;
            if (linker[i][0] < mid)
            {
                linker[i][++linker[i][0]] = u;
                return true;
            }
            for (int j = 1 ; j <= linker[i][0] ; j++)
            {
                if (dfs(linker[i][j],mid))
                {
                    linker[i][j] = u;
                    return true;
                }
            }
        }
    }
    return false;
}

bool calcu(int mid)
{
    int ret = 0;
    for (int i = 0 ; i < NY ; i++) linker[i][0] = 0;
    for (int i = 0 ; i < NX ; i++)
    {
        memset(used,false,sizeof(used));
        if (!dfs(i,mid)) return false;
    }
    return true;
}

int main()
{
    int N,M;
    while (scanf("%d%d",&N,&M) != EOF)
    {
        if(N == 0 && M == 0) break;
        NX = N;
        NY = M;
        memset(G,false,sizeof(G));
        for (int i = 0 ; i < NX ; i++)
        {
            char ch;
            int x;
            scanf("%s",str);
            while(scanf("%d%c",&x,&ch) != EOF)
            {
                G[i][x] = true;
                if(ch == ‘\n‘) break;
            }
        }
        //for (int i = 1 ; i <= 3 ; i++) printf("%d",calcu(i));
        int L = 0,R = NX;
        int ans;
        while (L <= R)
        {
            int mid = (L + R) / 2;
            if (calcu(mid))
            {
                R = mid - 1;
                ans = mid;
            }
            else L = mid + 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

在一中网络流最大流写法

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 4010;
const int MAXM = MAXN * 500;
const int INF = 0x3f3f3f3f;
vector<pair<int,int> >res;
struct Edge
{
    int u,v,next;
    int cap,flow;
}edge[MAXM];
int head[MAXN],tot;
int gap[MAXN],dep[MAXN],cur[MAXN];
int N,M;
char str[MAXN];

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

void add_edge(int u,int v,int cap,int rcap = 0)
{
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].cap = cap;
    edge[tot].flow = 0;
    edge[tot].next = head[u];
    head[u] = tot++;

    edge[tot].u = v;
    edge[tot].v = u;
    edge[tot].cap = rcap;
    edge[tot].flow = 0;
    edge[tot].next = head[v];
    head[v] = tot++;
}

int Q[MAXN];
void BFS(int st,int ed)
{
    memset(dep,-1,sizeof(dep));
    memset(gap,0,sizeof(gap));
    gap[0] = 1;
    int front = 0 ,rear = 0;
    dep[ed] = 0;
    Q[rear++] = ed;
    while (front < rear)
    {
        int u = Q[front++];
        for (int i = head[u] ; i != -1 ; i = edge[i].next)
        {
            int v = edge[i].v;
            if (dep[v] != -1) continue;
            Q[rear++] = v;
            dep[v] = dep[u] + 1;
            gap[dep[v]]++;
        }
    }
}

int S[MAXN];
int sap(int st,int ed,int N)
{
    BFS(st,ed);
    memcpy(cur,head,sizeof(head));
    int top = 0;
    int u = st;
    int ans = 0;
    while (dep[st] < N)
    {
        if (u == ed)
        {
            int Min = INF;
            int inser;
            for (int i = 0 ; i < top ; i++)
            {
                if (Min > edge[S[i]].cap - edge[S[i]].flow)
                {
                    Min = edge[S[i]].cap - edge[S[i]].flow;
                    inser = i;
                }
            }
            for (int i = 0 ; i < top ; i++)
            {
                edge[S[i]].flow += Min;
                edge[S[i] ^ 1].flow -=Min;
            }
            ans += Min;
            top = inser;
            u = edge[S[top] ^ 1].v;
            continue;
        }
        bool flag = false;
        int v;
        for (int i = cur[u] ; i != -1 ; i = edge[i].next)
        {
            v = edge[i].v;
            if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u])
            {
                flag = true;
                cur[u] = i;
                break;
            }
        }
        if(flag)
        {
            S[top++] = cur[u];
            u = v;
            continue;
        }
        int Min = N;
        for (int i = head[u] ; i != -1 ; i = edge[i].next)
        {
            if (edge[i].cap - edge[i].flow && dep[edge[i].v] < Min)
            {
                Min = dep[edge[i].v];
                cur[u] = i;
            }
        }
        gap[dep[u]]--;
        if (!gap[dep[u]]) return ans;
        dep[u] = Min + 1;
        gap[dep[u]]++;
        if (u != st) u = edge[S[--top] ^ 1].v;
    }
    return ans;
}

bool calcu(int mid)
{
    init();
    int source = N + M,target = N + M + 1;
    for (int i = 0 ; i < M ; i++)
        add_edge(source,i,mid);
    for (int i = 0 ; i < N ; i++)
        add_edge(M + i,target,1);
    for (int i = 0 ; i < (int)res.size() ; i++)
    {
        add_edge(res[i].second,res[i].first + M,1);
    }
    int ret = sap(source,target,N + M + 2);
    if(ret < N) return false;
    return true;
}

int main()
{
    while (scanf("%d%d",&N,&M) != EOF)
    {
        if(N == 0 && M == 0) break;
        res.clear();
        for (int i = 0 ; i < N ; i++)
        {
            char ch;
            int x;
            scanf("%s",str);
            while(scanf("%d%c",&x,&ch) != EOF)
            {
                res.push_back(make_pair(i,x));
                if(ch == ‘\n‘) break;
            }
        }
       // for (int i = 1 ; i <= 3 ; i++) printf("%d",calcu(i));
       // puts("");
        int L = 0,R = N;
        int ans;
        while (L <= R)
        {
            int mid = (L + R) / 2;
            if (calcu(mid))
            {
                R = mid - 1;
                ans = mid;
            }
            else L = mid + 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

POJ 2112 Optimal Milking

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 310;
const int INF = 0x3f3f3f3f;
bool G[MAXN][MAXN];
int dis[MAXN][MAXN];
bool used[MAXN];
int linker[MAXN][MAXN];
int num[MAXN];
int NX,NY;
int K,C,M;

bool dfs(int u)
{
    for (int i = 1 ; i <= NY ; i++)
    {
        if(G[u][i] && !used[i])
        {
            used[i] = true;
            if (linker[i][0] < M)
            {
                linker[i][++linker[i][0]] = u;
                return true;
            }
            else
            {
                for (int j = 1 ; j <= M ; j++)
                {
                    if (dfs(linker[i][j]))
                    {
                        linker[i][j] = u;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

bool calcu(int mid)
{
    memset(G,false,sizeof(G));
    for (int i = K + 1 ; i <= C + K ; i++)
    {
        int l = i - K;
        for (int j = 1 ; j <= K; j++)
        {
            int r = j;
            if (dis[i][j] <= mid)
                G[l][r] = true;
        }
    }
    for (int i = 0 ; i <= NY ; i++) linker[i][0] = 0;
    int ret = 0;
    for (int i = 1 ; i <= NX ; i++)
    {
        memset(used,false,sizeof(used));
        if (dfs(i)) ret++;
    }
    if (ret < C) return false;
    return true;
}

int main()
{
    while (scanf("%d%d%d",&K,&C,&M) != EOF)
    {
        for (int i = 1 ; i <= K + C ; i++)
            for (int j = 1 ; j <= K + C ; j++)
        {
                scanf("%d",&dis[i][j]);
                if(i != j && dis[i][j] == 0) dis[i][j] = INF;
        }
        for (int k = 1 ; k <= K + C ; k++)
        {
            for (int i = 1 ; i <= K + C ; i++)
                for (int j = 1 ; j <= K + C ; j++)
                    dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
        }
        NX = C;
        NY = K;
        int L = 0 ,R = INF;
        int ans;
       // for (int i = 1 ; i <= 3 ; i++)printf("%d",calcu(i));puts("");
        while (L <= R)
        {
            int mid = (L + R) / 2;
            if (calcu(mid))
            {
                ans = mid;
                R = mid - 1;
            }
            else L = mid + 1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

POJ 3189 Steady Cow Assignment

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 3010;
const int INF = 0x3f3f3f3f;
int dis[MAXN][MAXN];
bool used[MAXN];
int linker[MAXN][MAXN];
int num[MAXN];
int NX,NY;
int N,B,L,R;
int love[MAXN][MAXN];

bool dfs(int u)
{
    for (int i = 1 ; i <= NY ; i++)
    {
        if(love[u][i] >= L && love[u][i] <= R && !used[i])
        {
            used[i] = true;
            if (linker[i][0] < num[i])
            {
                linker[i][++linker[i][0]] = u;
                return true;
            }
            else
            {
                for (int j = 1 ; j <= num[i] ; j++)
                {
                    if (dfs(linker[i][j]))
                    {
                        linker[i][j] = u;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

bool calcu()
{
    for (int i = 0 ; i <= NY ; i++) linker[i][0] = 0;
    for (int i = 1 ; i <= NX ; i++)
    {
        memset(used,false,sizeof(used));
        if (!dfs(i)) return false;
    }
    return true;
}

int main()
{
    while (scanf("%d%d",&N,&B) != EOF)
    {
        for (int i = 1 ; i <= N ; i++)
        {
            for (int j = 1 ; j <= B ; j++)
            {
                int x;
                scanf("%d",&x);
                love[i][x] = j;
            }
        }
        for (int i = 1 ; i <= B ; i++) scanf("%d",&num[i]);
        NX = N;
        NY = B;
        L = 1; R = 1;
        int ans = INF;
        while (L <= R && R <= B)
        {
            if (calcu())
            {
                ans = min(ans,R - L + 1);
                L++;
            }
            else R++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————

最大权KM匹配  这2道题都比较简单

模板:点从0开始编号

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 3010;
const int INF = 0x3f3f3f3f;
typedef int type;
int nx,ny;
type g[MAXN][MAXN];
int linker[MAXN];
type lx[MAXN],ly[MAXN];
type slack[MAXN];
bool visx[MAXN],visy[MAXN];
bool dfs(int x)
{
    visx[x] = true;
    for (int y = 0 ; y < ny ; y++)
    {
        if (visy[y]) continue;
        type tmp = lx[x] + ly[y] - g[x][y];
        if (tmp == 0)
        {
            visy[y] = true;
            if (linker[y] == -1 || dfs(linker[y]))
            {
                linker[y] = x;
                return true;
            }
        }
        else if (slack[y] > tmp)
            slack[y] = tmp;
    }
    return false;
}

type KM()
{
    memset(linker,-1,sizeof(linker));
    for (int i = 0 ;  i < MAXN ; i++) ly[i] = 0.0;
    for (int i = 0 ; i < nx ; i++)
    {
        lx[i] = -INF;
        for (int j = 0 ; j < ny ; j++)
            if (g[i][j] - lx[i] > 0)
                lx[i] = g[i][j];
    }
    for (int x = 0 ; x < nx ; x++)
    {
        for (int i = 0 ; i < ny ; i++) slack[i] = INF;
        while (true)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if (dfs(x)) break;
            type d = INF;
            for (int i = 0 ; i < ny ; i++)
            {
                if (!visy[i] && d - slack[i] > 0)
                    d = slack[i];
            }
            for (int i = 0 ; i < nx ; i++)
            {
                if (visx[i])
                    lx[i] -= d;
            }
            for (int i = 0 ; i < ny ; i++)
            {
                if (visy[i]) ly[i] += d;
                else slack[i] -= d;
            }
        }
    }
    type ret = 0;
    for (int i = 0 ; i < ny ; i++)
        if (linker[i] != -1) ret += g[linker[i]][i];
    return ret;
}

int main()
{
    int N;
    while (scanf("%d",&N) != EOF)
    {
        nx = N;
        ny = N;
        for (int i = 0 ; i < N ; i++)
            for (int j = 0 ; j < N ; j++) scanf("%d",&g[i][j]);
        printf("%d\n",KM());
    }
    return 0;
}

HDU 3488 Tour 最小权匹配+建图

每个点都在一个有向环内,那么对于每个环内的点,他的出度等于入度等于1

那么就可以拆点,一个入点,一个出点,然后就直接求最小权匹配

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 3010;
const int INF = 0x3f3f3f3f;
typedef int type;
int nx,ny;
type g[MAXN][MAXN];
int linker[MAXN];
type lx[MAXN],ly[MAXN];
type slack[MAXN];
bool visx[MAXN],visy[MAXN];
bool dfs(int x)
{
    visx[x] = true;
    for (int y = 0 ; y < ny ; y++)
    {
        if (visy[y]) continue;
        type tmp = lx[x] + ly[y] - g[x][y];
        if (tmp == 0)
        {
            visy[y] = true;
            if (linker[y] == -1 || dfs(linker[y]))
            {
                linker[y] = x;
                return true;
            }
        }
        else if (slack[y] > tmp)
            slack[y] = tmp;
    }
    return false;
}

type KM()
{
    memset(linker,-1,sizeof(linker));
    for (int i = 0 ;  i < MAXN ; i++) ly[i] = 0.0;
    for (int i = 0 ; i < nx ; i++)
    {
        lx[i] = -INF;
        for (int j = 0 ; j < ny ; j++)
            if (g[i][j] - lx[i] > 0)
                lx[i] = g[i][j];
    }
    for (int x = 0 ; x < nx ; x++)
    {
        for (int i = 0 ; i < ny ; i++) slack[i] = INF;
        while (true)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if (dfs(x)) break;
            type d = INF;
            for (int i = 0 ; i < ny ; i++)
            {
                if (!visy[i] && d - slack[i] > 0)
                    d = slack[i];
            }
            for (int i = 0 ; i < nx ; i++)
            {
                if (visx[i])
                    lx[i] -= d;
            }
            for (int i = 0 ; i < ny ; i++)
            {
                if (visy[i]) ly[i] += d;
                else slack[i] -= d;
            }
        }
    }
    type ret = 0;
    for (int i = 0 ; i < ny ; i++)
        if (linker[i] != -1) ret += g[linker[i]][i];
    return -ret;
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int N,M;
        scanf("%d%d",&N,&M);
        for (int i = 0 ; i <= N ; i++)
            for (int j = 0 ; j <= N ; j++) g[i][j] = -INF;
        nx = ny = N;
        for (int i = 0 ; i < M ; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            u--;
            v--;
            g[u][v] = max(g[u][v],-w);
        }
        printf("%d\n",KM());
    }
    return 0;
}

————————————————————————————————————————————————————————————————————————————————————————————

一般图匹配带花树

复制粘贴一下。我也不太懂。弱B

来自

http://fanhq666.blog.163.com/blog/static/8194342620120304463580/

在北京冬令营的时候,yby提到了“带花树开花”算法来解非二分图的最大匹配。

于是,我打算看看这是个什么玩意。其实之前,我已经对这个算法了解了个大概,但是。。。真的不敢去写。

有一个叫Galil Zvi的人(应该叫计算机科学家),写了篇论文:

Efficient Algorithms for Finding Maximal Matching in Graphs

(如果你在网上搜不到,可以:http://builtinclz.abcz8.com/art/2012/Galil%20Zvi.pdf

这篇论文真神啊,它解决了4个问题:

(一般图+二分图)的(最大匹配+最大权匹配)问题。

算法的思想、故事,请自己看论文吧。

这个论文告诉了我们很多有趣的东西,例如:

用Dinic实现的二分图匹配的时间复杂度其实是O(M*N^0.5),这也许能够解释为什么一般网络流算法比Hungry要快了。

另外,带花树算法的正确性的证明比较困难;而其时间复杂度是可以做到O(M*N^0.5)的,不过要详细实现,那么就快能到“ACM最长论文奖”了。

我写了一个实例代码:

http://builtinclz.abcz8.com/art/2012/ural1099.cpp

没错,这是用来解决URAL 1099 Work Schedule那题的。时间复杂度是O(N^3)

简述一下“带花树”算法吧:

它的核心思想还是找增广路。假设已经匹配好了一堆点,我们从一个没有匹配的节点s开始,使用BFS生成搜索树。每当发现一个节点u,如果u还没有被匹配,那么就可以进行一次成功的增广;否则,我们就把节点u和它的配偶v一同接到树上,之后把v丢进队列继续搜索。我们给每个在搜索树上的点一个类型:S或者T。当u把它的配偶v扔进队列的时候,我们把u标记为T型,v标记为S型。于是,搜索树的样子是这样的:

       s

      /  \

     a    b

     |    |

     c    d

    / \  / \

    e f  u j

    | |  | |

    i j  v k

其中,黑色竖线相连的两个点是已经匹配好的,蓝色斜线表示两个点之间有边,但是没有配对。T型的用红色,S型的用黑色。

这里有个小问题:一个S型点d在某一步扩展的时候发现了点u,如果u已经在搜索树上了(即,出现了环),怎么办?

我们规定,如果u的类型是T型,就无视这次发现;(这意味着我们找到了一个长度为偶数的环,直接无视)

       s

      /  \

     a    b

     |    |

     c    d   如果连出来的边是指向T型点的,就无视这个边。

    / \  / \

    e f<-  g

    | |    |

    i j    k

否则,我们找到了一个长度为奇数的环,就要进行一次“缩花”的操作!所谓缩花操作,就是把这个环缩成一个点。

       s

      /  \

     a    b

     |    |

     c    d

    / \  / \

    e f  | g

    | |  | |

    i u<-+ k

这个图缩花之后变成了5个点(一个大点,或者叫一朵花,加原来的4个点):

缩点完成之后,还要把原来环里面的T型点统统变成S型点,之后扔到队列里去。

  +-------------+

  |             |

  |     s       |

  |    /  \     |

  |   a    b    |

  |   |    |    |   现在是一个点了!还是一个S点。

  |   c    d    |

  |  / \  / \   |

+-|--  f--u  ---|---+

| |             |   |

| |             |   |

| |             |   |

| +-------------+   |

|                   |

e                   g

|                   |

i                   k

为什么能缩成一个点呢?我们看一个长度为奇数的环(例如上图中的s-b-d-u-f-c-a-),如果我们能够给它中的任意一个点找一个出度(配偶),那么环中的其他点正好可以配成对,这说明,每个点的出度都是等效的。例如,假设我们能够给图中的点d另找一个配偶(例如d'好了),那么,环中的剩下6个点正好能配成3对,一个不多,一个不少(算上d和d'一共4对刚刚好)。

a-s-b-d-d'         a s-b d-d'

 \    |       =>    \     

  c-f-u              c f-u

这就是我们缩点的思想来源。有一个劳苦功高的计算机科学家证明了:缩点之前和缩点之后的图是否有增广路的情况是相同的。

缩起来的点又叫一朵花(blossom).

注意到,组成一朵花的里面可能嵌套着更小的花。

当我们最终找到一条增广路的时候,要把嵌套着的花层层展开,还原出原图上的增广路出来。

嗯,现在你对实现这个算法有任何想法吗?

天啊,还要缩点……写死谁。。。。。。

我一开始也是这么想的。

我看了一眼网上某个大牛的程序,之后结合自己的想法,很努力地写出了一个能AC的版本。

实现的要点有什么呢?

首先,我们不“显式”地表示花。我们记录一个Next数组,表示最终增广的时候的路径上的后继。同时,我们维护一个并查集,表示每个点现在在以哪个点为根的花里(一个花被缩进另一朵花之后就不算花了)。还要记录每个点的标记。

主程序是一段BFS。对于每个由x发展出来的点y,分4种情况讨论:

1。xy是配偶(不说夫妻,这是非二分图。。。)或者xy现在是一个点(在一朵花里):直接无视

2。y是T型点:直接无视

3。y目前单身:太好了,进行增广!

4。y是一个S型点:缩点!缩点!

缩点的时候要进行的工作:

1。找x和y的LCA(的根)p。找LCA可以用各种方法。。。直接朴素也行。

2。在Next数组中把x和y接起来(表示它们形成环了!)

3。从x、y分别走到p,修改并查集使得它们都变成一家人,同时沿路把Next数组接起来。

Next数组很奇妙。每时每刻,它实际形成了若干个挂在一起的双向链表来表示一朵花内部的走法。

     ----

    /    \<--+

    |    |   |

    |    |<--+

    v    v

   ----------

  /          \

+-            --+

|               |

|               |

+---->s  <------+     

有权图的最大匹配怎么做?

看论文吧。。。用类似KM的方法,不过,是给每个花再来一个权值。真的很复杂。。。

有一个人写了代码,好像是GPL许可证。。。你最好想办法搜到它的网站来看看版权的问题;总之,我先贴出来:

http://builtinclz.abcz8.com/art/2012/mwmatching.py

URAL 1099 模板题

给出一个图,问你最多匹配几个点,输出匹配点

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 300;
int N; //点的个数,点的编号从1到N
bool Graph[MAXN][MAXN];
int Match[MAXN];
bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN];
int Head,Tail;
int Queue[MAXN];
int Start,Finish;
int NewBase;
int Father[MAXN],Base[MAXN];
int Count;//匹配数,匹配对数是Count/2

void CreateGraph()
{
    int u,v;
    memset(Graph,false,sizeof(Graph));
    scanf("%d",&N);
    while(scanf("%d%d",&u,&v) == 2)
    {
        Graph[u][v] = Graph[v][u] = true;
    }
}

void Push(int u)
{
    Queue[Tail] = u;
    Tail++;
    InQueue[u] = true;
}

int Pop()
{
    int res = Queue[Head];
    Head++;
    return res;
}

int FindCommonAncestor(int u,int v)
{
    memset(InPath,false,sizeof(InPath));
    while(true)
    {
        u = Base[u];
        InPath[u] = true;
        if(u == Start) break;
        u = Father[Match[u]];
    }
    while(true)
    {
        v = Base[v];
        if(InPath[v])break;
        v = Father[Match[v]];
    }
    return v;
}

void ResetTrace(int u)
{
    int v;
    while(Base[u] != NewBase)
    {
        v = Match[u];
        InBlossom[Base[u]] = InBlossom[Base[v]] = true;
        u = Father[v];
        if(Base[u] != NewBase) Father[u] = v;
    }
}

void BloosomContract(int u,int v)
{
    NewBase = FindCommonAncestor(u,v);
    memset(InBlossom,false,sizeof(InBlossom));
    ResetTrace(u);
    ResetTrace(v);
    if(Base[u] != NewBase) Father[u] = v;
    if(Base[v] != NewBase) Father[v] = u;
    for(int tu = 1; tu <= N; tu++)
        if(InBlossom[Base[tu]])
    {
            Base[tu] = NewBase;
            if(!InQueue[tu]) Push(tu);
    }
}

void FindAugmentingPath()
{
    memset(InQueue,false,sizeof(InQueue));
    memset(Father,0,sizeof(Father));
    for(int i = 1; i <= N; i++)
        Base[i] = i;
    Head = Tail = 1;
    Push(Start);
    Finish = 0;
    while(Head < Tail)
    {
        int u = Pop();
        for(int v = 1; v <= N; v++)
            if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v))
            {
                if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0))
                    BloosomContract(u,v);
                else if(Father[v] == 0)
                {
                    Father[v] = u;
                    if(Match[v] > 0)
                        Push(Match[v]);
                    else
                    {
                        Finish = v;
                        return;
                    }
                }
            }
    }
}

void AugmentPath()
{
    int u,v,w;
    u = Finish;
    while(u > 0)
    {
        v = Father[u];
        w = Match[v];
        Match[v] = u;
        Match[u] = v;
        u = w;
    }
}

void Edmonds()
{
    memset(Match,0,sizeof(Match));
    for(int u = 1; u <= N; u++)
        if(Match[u] == 0)
        {
            Start = u;
            FindAugmentingPath();
            if(Finish > 0)AugmentPath();
        }
}

void PrintMatch()
{
    Count = 0;
    for(int u = 1; u <= N; u++)
        if(Match[u] > 0)
            Count++;
    printf("%d\n",Count);
    for(int u = 1; u <= N; u++)
        if(u < Match[u])
            printf("%d %d\n",u,Match[u]);
}

int main()
{
    CreateGraph();//建图
    Edmonds();//进行匹配
    PrintMatch();//输出匹配数和匹配
    return 0;
}

HDU 4687 Boke and Tsukkomi
暴力删边,计算新图的最大匹配如果小于原图匹配-1.就不是最大匹配里的边。

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 70;
int N; //点的个数,点的编号从1到N
bool Graph[MAXN][MAXN];
int Match[MAXN];
bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN];
int Head,Tail;
int Queue[MAXN];
int Start,Finish;
int NewBase;
int Father[MAXN],Base[MAXN];
int Count;//匹配数,匹配对数是Count/2

void CreateGraph()
{
    int u,v;
    memset(Graph,false,sizeof(Graph));
    scanf("%d",&N);
    while(scanf("%d%d",&u,&v) == 2)
    {
        Graph[u][v] = Graph[v][u] = true;
    }
}

void Push(int u)
{
    Queue[Tail] = u;
    Tail++;
    InQueue[u] = true;
}

int Pop()
{
    int res = Queue[Head];
    Head++;
    return res;
}

int FindCommonAncestor(int u,int v)
{
    memset(InPath,false,sizeof(InPath));
    while(true)
    {
        u = Base[u];
        InPath[u] = true;
        if(u == Start) break;
        u = Father[Match[u]];
    }
    while(true)
    {
        v = Base[v];
        if(InPath[v])break;
        v = Father[Match[v]];
    }
    return v;
}

void ResetTrace(int u)
{
    int v;
    while(Base[u] != NewBase)
    {
        v = Match[u];
        InBlossom[Base[u]] = InBlossom[Base[v]] = true;
        u = Father[v];
        if(Base[u] != NewBase) Father[u] = v;
    }
}

void BloosomContract(int u,int v)
{
    NewBase = FindCommonAncestor(u,v);
    memset(InBlossom,false,sizeof(InBlossom));
    ResetTrace(u);
    ResetTrace(v);
    if(Base[u] != NewBase) Father[u] = v;
    if(Base[v] != NewBase) Father[v] = u;
    for(int tu = 1; tu <= N; tu++)
        if(InBlossom[Base[tu]])
    {
            Base[tu] = NewBase;
            if(!InQueue[tu]) Push(tu);
    }
}

void FindAugmentingPath()
{
    memset(InQueue,false,sizeof(InQueue));
    memset(Father,0,sizeof(Father));
    for(int i = 1; i <= N; i++)
        Base[i] = i;
    Head = Tail = 1;
    Push(Start);
    Finish = 0;
    while(Head < Tail)
    {
        int u = Pop();
        for(int v = 1; v <= N; v++)
            if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v))
            {
                if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0))
                    BloosomContract(u,v);
                else if(Father[v] == 0)
                {
                    Father[v] = u;
                    if(Match[v] > 0)
                        Push(Match[v]);
                    else
                    {
                        Finish = v;
                        return;
                    }
                }
            }
    }
}

void AugmentPath()
{
    int u,v,w;
    u = Finish;
    while(u > 0)
    {
        v = Father[u];
        w = Match[v];
        Match[v] = u;
        Match[u] = v;
        u = w;
    }
}

void Edmonds()
{
    memset(Match,0,sizeof(Match));
    for(int u = 1; u <= N; u++)
        if(Match[u] == 0)
        {
            Start = u;
            FindAugmentingPath();
            if(Finish > 0)AugmentPath();
        }
}

int PrintMatch()
{
    Edmonds();
    Count = 0;
    for(int u = 1; u <= N; u++)
        if(Match[u] > 0)
            Count++;
    return Count / 2;
}

bool g[MAXN][MAXN];
vector<pair<int,int> >res;
int main()
{
    int M;
    while (scanf("%d%d",&N,&M) != EOF)
    {
        res.clear();
        memset(g,false,sizeof(g));
        memset(Graph,false,sizeof(Graph));
        for (int i = 1 ; i <= M ; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            res.push_back(make_pair(u,v));
            g[u][v] = g[v][u] = true;
            Graph[u][v] = Graph[v][u] = true;
        }
        int tot = PrintMatch();
        vector<int>ans;
        for (int i = 0 ; i < M ; i++)
        {
            int u = res[i].first;
            int v = res[i].second;
            memcpy(Graph,g,sizeof(g));
            for (int j = 1 ; j <= N ; j++)
               Graph[u][j] = Graph[j][u] = Graph[v][j] = Graph[j][v] = false;
            int ret = PrintMatch();
            if (ret < tot - 1) ans.push_back(i + 1);
        }
        printf("%d\n",ans.size());
        int sz = ans.size();
        for (int i = 0 ; i < sz ; i++)
        {
            printf("%d",ans[i]);
            if (i < sz - 1) putchar(‘ ‘);
        }
        puts("");
    }
    return 0;
}

时间: 2024-10-24 15:10:05

kuangbin带你飞 匹配问题 二分匹配 + 二分图多重匹配 + 二分图最大权匹配 + 一般图匹配带花树的相关文章

[kuangbin]带你飞之&#39;匹配问题&#39;专题

带飞网址 ? 专题十 匹配问题 √ HDU 1045 Fire NetHDU 2444 The Accomodation of StudentsHDU 1083 CoursesHDU 1281 棋盘游戏HDU 2819 SwapHDU 2389 Rain on your ParadeHDU 4185 Oil SkimmingPOJ 3020 Antenna PlacementHDU 1054 Strategic GameHDU 1151 Air RaidPOJ 2594 Treasure Exp

[kuangbin带你飞]专题十 匹配问题 二分图最大权匹配

二分图最大权匹配有km算法和网络流算法 km算法模板默认解决最大权匹配的问题 而使用最小费用最大流 是解决最小权匹配问题 这两种办法都可以求最大最小权 需要两次取反 TAT 感觉讲km会很难的样子... P hdu2255 km的模板题 #include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #include<map> #include<string

「kuangbin带你飞」专题十八 后缀数组

layout: post title: 「kuangbin带你飞」专题十八 后缀数组 author: "luowentaoaa" catalog: true tags: - kuangbin - 字符串 - 后缀数组 传送门 倍增法 struct DA{ bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } int t1[maxn],t2[maxn],c[maxn]; int r

Tarjan 联通图 Kuangbin 带你飞 联通图题目及部分联通图题目

Tarjan算法就不说了 想学看这 https://www.byvoid.com/blog/scc-tarjan/ https://www.byvoid.com/blog/biconnect/ 下面是几份基本的模版 首先是无向图割点桥的代码 下面的代码是用于求割点数目的 其中add_block[u] = x  表示删除u点之后增加的联通块个数.注意是增加的联通块个数 const int MAXN = 1010; const int MAXM = 10010; const int INF = 0x

「kuangbin带你飞」专题二十二 区间DP

layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - kuangbin - 区间DP - 动态规划 传送门 B.LightOJ - 1422 Halloween Costumes 题意 按顺序参加舞会,参加一个舞会要穿一种衣服,可以在参加完一个舞会后套上另一个衣服再去参加舞会,也可以在参加一个舞会的时候把外面的衣服脱了,脱到合适的衣服,但是脱掉的衣服不能再穿,参加完

HDU 4687 Boke and Tsukkomi(一般图匹配|带花树)

比赛的时候刚开始看这题还以为是二分图匹配,后来才发现根本不是,因为该题存在长度为奇数的圈 .  比如1->2,2->3,3->1 . 所以该题要用一般图匹配,即带花树算法 . 比赛时抄的模板有地方抄错了,上述样例出现了死循环 .   赛后补题的时候用map去重却得不到正确答案,不知为何,暂放 ,下面给出一种正确解法. 细节参见代码: #include<cstdio> #include<cstring> #include<iostream> #inclu

Poj 2289 Jamie&#39;s Contact Groups (二分+二分图多重匹配)

题目链接: Poj 2289 Jamie's Contact Groups 题目描述: 给出n个人的名单和每个人可以被分到的组,问将n个人分到m个组内,并且人数最多的组人数要尽量少,问人数最多的组有多少人? 解题思路: 二分图多重匹配相对于二分匹配来说不再是节点间的一一对应,而是Xi可以对应多个Yi.所以我们就需要一个限制(Xi最多匹配几个Yi).当Yi需要匹配Xi的时候,Xi的匹配未到上限,直接匹配,否则进行增广路.其实是二分图多重匹配的模板题,再套一个二分枚举最多组的人数就OK咯.下面就上板

HDU 4687 Boke and Tsukkomi 一般图匹配,带花树,思路,输出注意空行 难度:4

http://acm.hdu.edu.cn/showproblem.php?pid=4687 此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti, 则任意一个极大匹配中都必然有si或ti至少一个点被匹配,当在图中去掉si,ti两个点时,匹配数会损失一个或两个. 如果损失两个,就说明在极大匹配中这两个点分别连接不同的边,于是边i是无用的 所以总体思路:一般图匹配求出最大匹配数cnt0,分别试着去掉每条边的端点,再次匹配,匹配数如果小于cnt0-1,则这条边无

Jamie&#39;s Contact Groups(二分图多重匹配+二分)(网络流)

Jamie's Contact Groups Time Limit:7000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2289 Description Jamie is a very popular girl and has quite a lot of friends, so she always keeps a very long contact list