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;
}
View Code

 

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

 

 

 

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;
}
View Code

第二份 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;
}
View Code

用第一道题来表示这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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

还有树形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;
}
View Code

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;
}
View Code

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;
}
View Code


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;
}
View Code

 

 

 

 

 

 

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

二分图多重匹配

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;
}
View Code

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;
}
View Code

在一中网络流最大流写法

#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;
}
View Code

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;
}
View Code

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;
}
View Code

 

 

 

 

 

 

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

最大权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;
}
View Code


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;
}
View Code

 

 

 

 

 

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

一般图匹配带花树

复制粘贴一下。我也不太懂。弱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
      /  
         
     |    |
     c    d
        
       u j
    | |  | |
    i j  v k
其中,黑色竖线相连的两个点是已经匹配好的,蓝色斜线表示两个点之间有边,但是没有配对。T型的用红色,S型的用黑色。
 
这里有个小问题:一个S型点d在某一步扩展的时候发现了点u,如果u已经在搜索树上了(即,出现了环),怎么办?
我们规定,如果u的类型是T型,就无视这次发现;(这意味着我们找到了一个长度为偶数的环,直接无视)
       s
      /  
         
     |    |
     c    d   如果连出来的边是指向T型点的,就无视这个边。
        
       
    | |    |
    i j    k
否则,我们找到了一个长度为奇数的环,就要进行一次“缩花”的操作!所谓缩花操作,就是把这个环缩成一个点。
       s
      /  
         
     |    |
     c    d
        
        
    | |   |
    i u<-+ k
这个图缩花之后变成了5个点(一个大点,或者叫一朵花,加原来的4个点):
缩点完成之后,还要把原来环里面的T型点统统变成S型点,之后扔到队列里去。
  +-------------+
  |             |
  |     s       |
  |    /  \     
  |           
  |   |    |    |   现在是一个点了!还是一个S点。
  |   c    d    |
  |     / \   
+-|--  --u  ---|---+
| |             |   |
| |             |   |
| |             |   |
| +-------------+   |
|                   |
e                   g
|                   |
i                   k
为什么能缩成一个点呢?我们看一个长度为奇数的环(例如上图中的s-b-d-u-f-c-a-),如果我们能够给它中的任意一个点找一个出度(配偶),那么环中的其他点正好可以配成对,这说明,每个点的出度都是等效的。例如,假设我们能够给图中的点d另找一个配偶(例如d'好了),那么,环中的剩下6个点正好能配成3对,一个不多,一个不少(算上d和d'一共4对刚刚好)。
a-s-b-dd'         a s-b d-d'
 \    |       =>    \     
  cf-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数组很奇妙。每时每刻,它实际形成了若干个挂在一起的双向链表来表示一朵花内部的走法。
     ----
    /    \--+
    |    |   |
    |    |--+
        
   ----------
  /          \
+-            --+
|               |
|               |
+----s  ------+     
 
 
 
有权图的最大匹配怎么做?
看论文吧。。。用类似KM的方法,不过,是给每个花再来一个权值。真的很复杂。。。
有一个人写了代码,好像是GPL许可证。。。你最好想办法搜到它的网站来看看版权的问题;总之,我先贴出来:

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;
}
View Code

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;
}
View Code

 

UVALIVE 5962 Strongly Connected Chemicals

题意:一堆阴离子阳离子,给出阴阳离子吸引关系。求最大的集合满足所有阳离子可以吸附所有阴离子。但是有一个限制最大的集合至少有一对阴阳离子

solotion:很容易想到建立补图求最大独立及。但是注意无法满足条件 。于是就暴力枚举一定在图里面的阴阳离子。

假设枚举a b 然后找到所有的和a可以共存的阴离子(设为x集合)以及和b共存的阳离子(设为y集合) 

现在只需求x集合中和y集合中最多有多少个离子可以共存 这个求最大独立集就行了 枚举所有的a b 取最大值

 

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 110;
char G[MAXN][MAXN];
int N,M;
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];
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,kase = 1;
    scanf("%d",&T);
    while (T--) {
        scanf("%d%d",&N,&M);
        for (int i = 1 ; i <= N ; i++) scanf("%s",G[i] + 1);
        int ans = 0;
        for (int i = 1 ; i <= N ; i++) {
            for (int j = 1 ; j <= M ; j++) {
                if (G[i][j] == '0') continue;
                int cnt1 = 0,cnt2 = 0;
                for (int p = 1 ; p <= N ; p++) if (G[p][j] == '1') cnt1++;
                for (int q = 1 ; q <= M ; q++) if (G[i][q] == '1') cnt2++;
                init();
                for (int p = 1 ; p <= N ; p++) {
                    if (G[p][j] == '0') continue;
                    for (int  q = 1 ; q <= M ; q++) {
                        if (G[i][q] == '1' && G[p][q] == '0')
                            add_edge(p,q,97);
                    }
                }
                ans = max(ans,cnt1 + cnt2 - calcu());
            }
        }
        printf("Case %d: %d\n", kase++, ans);
    }
    return 0;
}
View Code

 

posted @ 2015-10-29 20:04  Commence  阅读(815)  评论(0编辑  收藏  举报