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

二分图又称作二部图,是图论中的一种特殊模型。 设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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (color[v] == color[u]) return false;
if (!color[v])
{
color[v] = - color[u];
if (!bipartite(v)) return false;
}
}
return true;
} bool dfs(int u)
{
for (int i = head[u]; i != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
memset(linker,-,sizeof(linker));
int ret = ;
for (int i = ; 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,,sizeof(color));
for (int i = ; i < M ; i++)
{
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v,);
// add_edge(v,u,9797);
}
bool flag = false;
for (int i = ; i <= N ; i++)
{
if (color[i] == )
{
color[i] = ;
if (!bipartite(i))
{
flag = true;
break;
}
}
}
if (!flag)
{
printf("%d\n",calcu());
}
else puts("No");
}
return ;
}

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

2份模板

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

const int MAXN = ;
const int MAXM = ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; i <= N ; i++)
{
memset(used,false,sizeof(used));
if (dfs(i)) ret++;
}
return ret;
}

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

const int MAXN = ;
const int MAXM = ;
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,-,sizeof(dx));
memset(dy,-,sizeof(dy));
for (int i = ; i < uN ; i++)
{
if (MX[i] == -)
{
q.push(i);
dx[i] = ;
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
if (dx[u] > dis) break;
int sz = G[u].size();
for (int i = ; i < sz ; i++)
{
int v = G[u][i];
if (dy[v] == -)
{
dy[v] = dx[u] + ;
if (MY[v] == -) dis = dy[v];
else
{
dx[MY[v]] = dy[v] + ;
q.push(MY[v]);
}
}
}
}
return dis != INF;
} bool dfs(int u)
{
int sz = G[u].size();
for (int i = ; i < sz ; i++)
{
int v = G[u][i];
if (!used[v] && dy[v] == dx[u] + )
{
used[v] = true;
if (MY[v] != - && dy[v] == dis) continue;
if (MY[v] == - || dfs(MY[v]))
{
MY[v] = u;
MX[u] = v;
return true;
}
}
}
return false;
} int Maxmatch()
{
int ret = ;
memset(MX,-,sizeof(MX));
memset(MY,-,sizeof(MY));
while (searchp())
{
memset(used,false,sizeof(used));
for (int i = ; i < uN; i++)
if (MX[i] == - && 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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; i <= N ; i++)
{
memset(used,false,sizeof(used));
if (dfs(i)) ret++;
}
return ret;
} char G[][];
int idrow[][],idcol[][];
int main()
{
// freopen("sample.txt","r",stdin);
while (scanf("%d",&n) != EOF)
{
if (n == ) break;
for (int i = ; i <= n ; i++) scanf("%s",G[i] + );
init();
int idx = ,idy = ;
for (int i = ; i <= n ; i++)
{
for (int j = ; 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 = ; i <= n ; i++)
{
for (int j = ; 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 - ;
for (int i = ; i <= n ; i++)
{
for (int j = ; j <= n ; j++)
{
if (G[i][j] == '.')
add_edge(idrow[i][j],idcol[i][j],);
}
}
printf("%d\n",calcu());
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = ;
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,-,sizeof(dx));
memset(dy,-,sizeof(dy));
for (int i = ; i < uN ; i++)
{
if (MX[i] == -)
{
q.push(i);
dx[i] = ;
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
if (dx[u] > dis) break;
int sz = G[u].size();
for (int i = ; i < sz ; i++)
{
int v = G[u][i];
if (dy[v] == -)
{
dy[v] = dx[u] + ;
if (MY[v] == -) dis = dy[v];
else
{
dx[MY[v]] = dy[v] + ;
q.push(MY[v]);
}
}
}
}
return dis != INF;
} bool dfs(int u)
{
int sz = G[u].size();
for (int i = ; i < sz ; i++)
{
int v = G[u][i];
if (!used[v] && dy[v] == dx[u] + )
{
used[v] = true;
if (MY[v] != - && dy[v] == dis) continue;
if (MY[v] == - || dfs(MY[v]))
{
MY[v] = u;
MX[u] = v;
return true;
}
}
}
return false;
} int Maxmatch()
{
int ret = ;
memset(MX,-,sizeof(MX));
memset(MY,-,sizeof(MY));
while (searchp())
{
memset(used,false,sizeof(used));
for (int i = ; i < uN; i++)
if (MX[i] == - && dfs(i)) ret++;
}
return ret;
} char g[][];
int n;
int idrow[][],idcol[][];
int main()
{
//freopen("sample.txt","r",stdin);
while (scanf("%d",&n) != EOF)
{
if (n == ) break;
for (int i = ; i <= n * n ; i++)G[i].clear();
for (int i = ; i <= n ; i++) scanf("%s",g[i] + );
int idx = ,idy = ;
for (int i = ; i <= n ; i++)
{
for (int j = ; 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 = ; i <= n ; i++)
{
for (int j = ; 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 = ; i <= n ; i++)
{
for (int j = ; 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 ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; 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 = ; i <= P ; i++)
{
int cnt;
scanf("%d",&cnt);
while(cnt--)
{
int x;
scanf("%d",&x);
add_edge(i,x,);
}
}
printf("%s\n",calcu() >= P ? "YES" : "NO");
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int INF = 0x3f3f3f3f;
const int MAXM = MAXN * MAXN;
int N,M,K;
int G[MAXN][MAXN]; void init()
{
memset(G,,sizeof(G));
} bool used[MAXN];
int linker[MAXN],rlinker[MAXN];
int important; bool dfs(int u,bool first)
{
for (int i = ; i <= N ; i++)
{
if (!G[u][i]) continue;
int v = i;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v],first))
{
if (first)
{
linker[v] = u;
rlinker[u] = v;
}
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
memset(rlinker,-,sizeof(rlinker));
for (int i = ; i <= N ; i++)
{
memset(used,false,sizeof(used));
if (dfs(i,true)) ret++;
}
return ret;
} bool judge()
{
for (int i = ; i <= N ; i++)
{
if (rlinker[i] != -) continue;
memset(used,false,sizeof(used));
if (dfs(i,false)) return false;
}
return true;
} int main()
{
//freopen("sample.txt","r",stdin);
int kase = ;
while (scanf("%d%d%d",&N,&M,&K) != EOF)
{
init();
for (int i = ; i < K ; i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u][v] = true;
}
int num = calcu();
important = ;
for (int u = ; u <= N ; u++)
{
if (rlinker[u] == -) continue;
int v = rlinker[u];
G[u][v] = false;
linker[v] = -;
rlinker[u] = -;
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 ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot;
int N; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; 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 = ; i <= N ; i++)
{
for (int j = ; j <= N; j++)
{
int x;
scanf("%d",&x);
if(x) add_edge(i,j,);
}
}
int ret = calcu();
if (ret < N) puts("-1");
else
{
for (int i = ; i <= N ; i++)
{
int j;
for (j = ; 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 = ; i < (int)ans.size() ; i++)
{
printf("C %d %d\n",ans[i].first,ans[i].second);
}
}
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = ;
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,-,sizeof(dx));
memset(dy,-,sizeof(dy));
for (int i = ; i < uN ; i++)
{
if (MX[i] == -)
{
q.push(i);
dx[i] = ;
}
}
while (!q.empty())
{
int u = q.front();
q.pop();
if (dx[u] > dis) break;
int sz = G[u].size();
for (int i = ; i < sz ; i++)
{
int v = G[u][i];
if (dy[v] == -)
{
dy[v] = dx[u] + ;
if (MY[v] == -) dis = dy[v];
else
{
dx[MY[v]] = dy[v] + ;
q.push(MY[v]);
}
}
}
}
return dis != INF;
} bool dfs(int u)
{
int sz = G[u].size();
for (int i = ; i < sz ; i++)
{
int v = G[u][i];
if (!used[v] && dy[v] == dx[u] + )
{
used[v] = true;
if (MY[v] != - && dy[v] == dis) continue;
if (MY[v] == - || dfs(MY[v]))
{
MY[v] = u;
MX[u] = v;
return true;
}
}
}
return false;
} int Maxmatch()
{
int ret = ;
memset(MX,-,sizeof(MX));
memset(MY,-,sizeof(MY));
while (searchp())
{
memset(used,false,sizeof(used));
for (int i = ; i < uN; i++)
if (MX[i] == - && 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 = ;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&Time,&M);
for (int i = ; i < M ; i++)
scanf("%d%d%d",&X[i],&Y[i],&v[i]);
for (int i = ; i < M ; i++)G[i].clear();
uN = M;
scanf("%d",&N);
for (int i = ; i < N ; i++)
scanf("%d%d",&umbx[i],&umby[i]);
for (int i = ; i < M ; i++)
{
for (int j = ; 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 ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
const int dx[] = {,,-,};
const int dy[] = {,-,,};
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot;
int n;
int N,M; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; 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 = ;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
memset(id,,sizeof(id));
int idx = ;
for (int i = ; i <= n ; i++)
scanf("%s",G[i] + );
for (int i = ; i <= n ; i++)
{
for (int j = ; j <= n ; j++)
{
if (G[i][j] == '#') id[i][j] = idx++;
}
}
N = idx - ;
init();
for (int i = ; i <= n ; i++)
{
for (int j = ; j <= n ; j++)
{
for (int d = ; d < ; d++)
{
int nx = i + dx[d];
int ny = j + dy[d];
if (nx >= && nx <= n && ny >= && ny <= n && G[nx][ny] == '#')
{
add_edge(id[i][j],id[nx][ny],);
}
}
}
}
printf("Case %d: %d\n",kase++,calcu() / );
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
const int dx[] = {,,-,};
const int dy[] = {,-,,};
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot;
int un,vn; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; i <= un ; i++)
{
memset(used,false,sizeof(used));
if (dfs(i)) ret++;
}
return ret++;
} int N,M;
int id[][];
char G[][]; int main()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&N,&M);
for (int i = ; i <= N ; i++) scanf("%s",G[i] + );
int leap = ;
for (int i = ; i <= N ; i++)
{
for (int j = ; j <= M ; j++)
{
if (G[i][j] == '*') id[i][j] = ++leap;
}
}
init();
un = leap;
for (int i = ; i <= N ; i++)
{
for (int j = ; j <= M ; j++)
{
if (G[i][j] != '*') continue;
for (int d = ; d < ; d++)
{
int nx = i + dx[d];
int ny = j + dy[d];
if (nx >= && nx <= N && ny >= && ny <= M && G[nx][ny] == '*')
{
add_edge(id[i][j],id[nx][ny],);
}
}
}
}
printf("%d\n",leap - calcu() / );
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot;
int un,vn; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; 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 = ; i <= N ; i++)
{
int u,cnt;
scanf("%d:(%d)",&u,&cnt);
while (cnt--)
{
int x;
scanf("%d",&x);
add_edge(u,x,);
add_edge(x,u,);
// printf("%d %d\n",u,x);
}
}
printf("%d\n",calcu() / );
}
return ;
}

还有树形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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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][]; 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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (v == fa) continue;
if (sta == )
dp[u][sta] += dfs(v,,u);
else dp[u][sta] += min(dfs(v,,u),dfs(v,,u));
}
return dp[u][sta];
} int main()
{
while (scanf("%d",&N) != EOF)
{
init();
int root = ;
for (int i = ; i <= N ; i++)
{
int u,cnt;
scanf("%d:(%d)",&u,&cnt);
if (cnt > ) root = u;
while (cnt--)
{
int x;
scanf("%d",&x);
add_edge(u,x,);
add_edge(x,u,);
// printf("%d %d\n",u,x);
}
}
memset(dp,0x3f,sizeof(dp));
int ans = min(dfs(root,,-),dfs(root,,-));
printf("%d\n",ans);
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot;
int N,M; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; 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,);
}
printf("%d\n",N - calcu());
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
int N,M;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; 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 == && M == ) break;
memset(G,0x3f,sizeof(G));
for (int i = ; i <= M ; i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u][v] = ;
}
for (int k = ; k <= N ; k++)
{
for (int i = ; i <= N ; i++)
{
for (int j = ; j <= N ; j++)
G[i][j] = min(G[i][j],G[i][k] + G[k][j]);
}
}
init();
for (int i = ; i <= N ; i++)
{
for (int j = ; j <= N ; j++)
{
if (G[i][j] < INF)
add_edge(i,j,);
}
}
printf("%d\n",N - calcu());
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * MAXN * ;
const int INF = 0x3f3f3f3f;
int N,M;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; 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 = ; i < N ; i++)
{
cin >> like[i] >> dislike[i];
}
for (int i = ; i < N ; i++)
{
for (int j = ; j < N ; j++)
{
if (i == j) continue;
if (like[i] == dislike[j] || dislike[i] == like[j])
add_edge(i,j,);
}
}
printf("%d\n",N - calcu() / );
}
return ;
}

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

二分图多重匹配

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

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

模板

const int MAXN = ;
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 = ; i <= NY ; i++)
{
if(G[u][i] && !used[i])
{
used[i] = true;
if (linker[i][] < num[i])
{
linker[i][++linker[i][]] = u;
return true;
}
else
{
for (int j = ; j <= num[i] ; j++)
{
if (dfs(linker[i][j]))
{
linker[i][j] = u;
return true;
}
}
}
}
}
return false;
} int calcu(int mid)
{
for (int i = ; i <= NY ; i++) linker[i][] = ;
int ret = ;
for (int i = ; 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 == ? b : gcd(b, a % b);}
const int MAXN = ;
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 = ; i < NY ; i++)
{
if (G[u][i] && !used[i])
{
used[i] = true;
if (linker[i][] < mid)
{
linker[i][++linker[i][]] = u;
return true;
}
for (int j = ; j <= linker[i][] ; j++)
{
if (dfs(linker[i][j],mid))
{
linker[i][j] = u;
return true;
}
}
}
}
return false;
} bool calcu(int mid)
{
int ret = ;
for (int i = ; i < NY ; i++) linker[i][] = ;
for (int i = ; 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 == && M == ) break;
NX = N;
NY = M;
memset(G,false,sizeof(G));
for (int i = ; 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 = ,R = NX;
int ans;
while (L <= R)
{
int mid = (L + R) / ;
if (calcu(mid))
{
R = mid - ;
ans = mid;
}
else L = mid + ;
}
printf("%d\n",ans);
}
return ;
}

在一中网络流最大流写法

#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 == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXM = MAXN * ;
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,-,sizeof(head));
tot = ;
} void add_edge(int u,int v,int cap,int rcap = )
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].cap = cap;
edge[tot].flow = ;
edge[tot].next = head[u];
head[u] = tot++; edge[tot].u = v;
edge[tot].v = u;
edge[tot].cap = rcap;
edge[tot].flow = ;
edge[tot].next = head[v];
head[v] = tot++;
} int Q[MAXN];
void BFS(int st,int ed)
{
memset(dep,-,sizeof(dep));
memset(gap,,sizeof(gap));
gap[] = ;
int front = ,rear = ;
dep[ed] = ;
Q[rear++] = ed;
while (front < rear)
{
int u = Q[front++];
for (int i = head[u] ; i != - ; i = edge[i].next)
{
int v = edge[i].v;
if (dep[v] != -) continue;
Q[rear++] = v;
dep[v] = dep[u] + ;
gap[dep[v]]++;
}
}
} int S[MAXN];
int sap(int st,int ed,int N)
{
BFS(st,ed);
memcpy(cur,head,sizeof(head));
int top = ;
int u = st;
int ans = ;
while (dep[st] < N)
{
if (u == ed)
{
int Min = INF;
int inser;
for (int i = ; 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 = ; i < top ; i++)
{
edge[S[i]].flow += Min;
edge[S[i] ^ ].flow -=Min;
}
ans += Min;
top = inser;
u = edge[S[top] ^ ].v;
continue;
}
bool flag = false;
int v;
for (int i = cur[u] ; i != - ; i = edge[i].next)
{
v = edge[i].v;
if (edge[i].cap - edge[i].flow && dep[v] + == 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 != - ; 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 + ;
gap[dep[u]]++;
if (u != st) u = edge[S[--top] ^ ].v;
}
return ans;
} bool calcu(int mid)
{
init();
int source = N + M,target = N + M + ;
for (int i = ; i < M ; i++)
add_edge(source,i,mid);
for (int i = ; i < N ; i++)
add_edge(M + i,target,);
for (int i = ; i < (int)res.size() ; i++)
{
add_edge(res[i].second,res[i].first + M,);
}
int ret = sap(source,target,N + M + );
if(ret < N) return false;
return true;
} int main()
{
while (scanf("%d%d",&N,&M) != EOF)
{
if(N == && M == ) break;
res.clear();
for (int i = ; 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 = ,R = N;
int ans;
while (L <= R)
{
int mid = (L + R) / ;
if (calcu(mid))
{
R = mid - ;
ans = mid;
}
else L = mid + ;
}
printf("%d\n",ans);
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
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 = ; i <= NY ; i++)
{
if(G[u][i] && !used[i])
{
used[i] = true;
if (linker[i][] < M)
{
linker[i][++linker[i][]] = u;
return true;
}
else
{
for (int j = ; 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 + ; i <= C + K ; i++)
{
int l = i - K;
for (int j = ; j <= K; j++)
{
int r = j;
if (dis[i][j] <= mid)
G[l][r] = true;
}
}
for (int i = ; i <= NY ; i++) linker[i][] = ;
int ret = ;
for (int i = ; 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 = ; i <= K + C ; i++)
for (int j = ; j <= K + C ; j++)
{
scanf("%d",&dis[i][j]);
if(i != j && dis[i][j] == ) dis[i][j] = INF;
}
for (int k = ; k <= K + C ; k++)
{
for (int i = ; i <= K + C ; i++)
for (int j = ; j <= K + C ; j++)
dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
}
NX = C;
NY = K;
int L = ,R = INF;
int ans;
// for (int i = 1 ; i <= 3 ; i++)printf("%d",calcu(i));puts("");
while (L <= R)
{
int mid = (L + R) / ;
if (calcu(mid))
{
ans = mid;
R = mid - ;
}
else L = mid + ;
}
printf("%d\n",ans);
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
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 = ; i <= NY ; i++)
{
if(love[u][i] >= L && love[u][i] <= R && !used[i])
{
used[i] = true;
if (linker[i][] < num[i])
{
linker[i][++linker[i][]] = u;
return true;
}
else
{
for (int j = ; j <= num[i] ; j++)
{
if (dfs(linker[i][j]))
{
linker[i][j] = u;
return true;
}
}
}
}
}
return false;
} bool calcu()
{
for (int i = ; i <= NY ; i++) linker[i][] = ;
for (int i = ; 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 = ; i <= N ; i++)
{
for (int j = ; j <= B ; j++)
{
int x;
scanf("%d",&x);
love[i][x] = j;
}
}
for (int i = ; i <= B ; i++) scanf("%d",&num[i]);
NX = N;
NY = B;
L = ; R = ;
int ans = INF;
while (L <= R && R <= B)
{
if (calcu())
{
ans = min(ans,R - L + );
L++;
}
else R++;
}
printf("%d\n",ans);
}
return ;
}

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

最大权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 == ? b : gcd(b, a % b);}
const int MAXN = ;
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 = ; y < ny ; y++)
{
if (visy[y]) continue;
type tmp = lx[x] + ly[y] - g[x][y];
if (tmp == )
{
visy[y] = true;
if (linker[y] == - || dfs(linker[y]))
{
linker[y] = x;
return true;
}
}
else if (slack[y] > tmp)
slack[y] = tmp;
}
return false;
} type KM()
{
memset(linker,-,sizeof(linker));
for (int i = ; i < MAXN ; i++) ly[i] = 0.0;
for (int i = ; i < nx ; i++)
{
lx[i] = -INF;
for (int j = ; j < ny ; j++)
if (g[i][j] - lx[i] > )
lx[i] = g[i][j];
}
for (int x = ; x < nx ; x++)
{
for (int i = ; 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 = ; i < ny ; i++)
{
if (!visy[i] && d - slack[i] > )
d = slack[i];
}
for (int i = ; i < nx ; i++)
{
if (visx[i])
lx[i] -= d;
}
for (int i = ; i < ny ; i++)
{
if (visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
}
type ret = ;
for (int i = ; i < ny ; i++)
if (linker[i] != -) ret += g[linker[i]][i];
return ret;
} int main()
{
int N;
while (scanf("%d",&N) != EOF)
{
nx = N;
ny = N;
for (int i = ; i < N ; i++)
for (int j = ; j < N ; j++) scanf("%d",&g[i][j]);
printf("%d\n",KM());
}
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
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 = ; y < ny ; y++)
{
if (visy[y]) continue;
type tmp = lx[x] + ly[y] - g[x][y];
if (tmp == )
{
visy[y] = true;
if (linker[y] == - || dfs(linker[y]))
{
linker[y] = x;
return true;
}
}
else if (slack[y] > tmp)
slack[y] = tmp;
}
return false;
} type KM()
{
memset(linker,-,sizeof(linker));
for (int i = ; i < MAXN ; i++) ly[i] = 0.0;
for (int i = ; i < nx ; i++)
{
lx[i] = -INF;
for (int j = ; j < ny ; j++)
if (g[i][j] - lx[i] > )
lx[i] = g[i][j];
}
for (int x = ; x < nx ; x++)
{
for (int i = ; 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 = ; i < ny ; i++)
{
if (!visy[i] && d - slack[i] > )
d = slack[i];
}
for (int i = ; i < nx ; i++)
{
if (visx[i])
lx[i] -= d;
}
for (int i = ; i < ny ; i++)
{
if (visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
}
type ret = ;
for (int i = ; i < ny ; i++)
if (linker[i] != -) 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 = ; i <= N ; i++)
for (int j = ; j <= N ; j++) g[i][j] = -INF;
nx = ny = N;
for (int i = ; 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 ;
}

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

一般图匹配带花树

复制粘贴一下。我也不太懂。弱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许可证。。。你最好想办法搜到它的网站来看看版权的问题;总之,我先贴出来:

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
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) == )
{
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 = ; tu <= N; tu++)
if(InBlossom[Base[tu]])
{
Base[tu] = NewBase;
if(!InQueue[tu]) Push(tu);
}
} void FindAugmentingPath()
{
memset(InQueue,false,sizeof(InQueue));
memset(Father,,sizeof(Father));
for(int i = ; i <= N; i++)
Base[i] = i;
Head = Tail = ;
Push(Start);
Finish = ;
while(Head < Tail)
{
int u = Pop();
for(int v = ; v <= N; v++)
if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v))
{
if((v == Start) || ((Match[v] > ) && Father[Match[v]] > ))
BloosomContract(u,v);
else if(Father[v] == )
{
Father[v] = u;
if(Match[v] > )
Push(Match[v]);
else
{
Finish = v;
return;
}
}
}
}
} void AugmentPath()
{
int u,v,w;
u = Finish;
while(u > )
{
v = Father[u];
w = Match[v];
Match[v] = u;
Match[u] = v;
u = w;
}
} void Edmonds()
{
memset(Match,,sizeof(Match));
for(int u = ; u <= N; u++)
if(Match[u] == )
{
Start = u;
FindAugmentingPath();
if(Finish > )AugmentPath();
}
} void PrintMatch()
{
Count = ;
for(int u = ; u <= N; u++)
if(Match[u] > )
Count++;
printf("%d\n",Count);
for(int u = ; u <= N; u++)
if(u < Match[u])
printf("%d %d\n",u,Match[u]);
} int main()
{
CreateGraph();//建图
Edmonds();//进行匹配
PrintMatch();//输出匹配数和匹配
return ;
}

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 == ? b : gcd(b, a % b);}
const int MAXN = ;
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) == )
{
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 = ; tu <= N; tu++)
if(InBlossom[Base[tu]])
{
Base[tu] = NewBase;
if(!InQueue[tu]) Push(tu);
}
} void FindAugmentingPath()
{
memset(InQueue,false,sizeof(InQueue));
memset(Father,,sizeof(Father));
for(int i = ; i <= N; i++)
Base[i] = i;
Head = Tail = ;
Push(Start);
Finish = ;
while(Head < Tail)
{
int u = Pop();
for(int v = ; v <= N; v++)
if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v))
{
if((v == Start) || ((Match[v] > ) && Father[Match[v]] > ))
BloosomContract(u,v);
else if(Father[v] == )
{
Father[v] = u;
if(Match[v] > )
Push(Match[v]);
else
{
Finish = v;
return;
}
}
}
}
} void AugmentPath()
{
int u,v,w;
u = Finish;
while(u > )
{
v = Father[u];
w = Match[v];
Match[v] = u;
Match[u] = v;
u = w;
}
} void Edmonds()
{
memset(Match,,sizeof(Match));
for(int u = ; u <= N; u++)
if(Match[u] == )
{
Start = u;
FindAugmentingPath();
if(Finish > )AugmentPath();
}
} int PrintMatch()
{
Edmonds();
Count = ;
for(int u = ; u <= N; u++)
if(Match[u] > )
Count++;
return Count / ;
} 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 = ; 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 = ; i < M ; i++)
{
int u = res[i].first;
int v = res[i].second;
memcpy(Graph,g,sizeof(g));
for (int j = ; j <= N ; j++)
Graph[u][j] = Graph[j][u] = Graph[v][j] = Graph[j][v] = false;
int ret = PrintMatch();
if (ret < tot - ) ans.push_back(i + );
}
printf("%d\n",ans.size());
int sz = ans.size();
for (int i = ; i < sz ; i++)
{
printf("%d",ans[i]);
if (i < sz - ) putchar(' ');
}
puts("");
}
return ;
}

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 = ;
char G[MAXN][MAXN];
int N,M;
const int MAXM = ;
const int INF = 0x3f3f3f3f;
struct Edge
{
int u,v,next;
int w;
}edge[MAXM];
int head[MAXN],tot; void init()
{
tot = ;
memset(head,-,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 != - ; i = edge[i].next)
{
int v = edge[i].v;
if (!used[v])
{
used[v] = true;
if (linker[v] == - || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
}
return false;
} int calcu()
{
int ret = ;
memset(linker,-,sizeof(linker));
for (int i = ; i <= N ; i++)
{
memset(used,false,sizeof(used));
if (dfs(i)) ret++;
}
return ret;
} int main() {
int T,kase = ;
scanf("%d",&T);
while (T--) {
scanf("%d%d",&N,&M);
for (int i = ; i <= N ; i++) scanf("%s",G[i] + );
int ans = ;
for (int i = ; i <= N ; i++) {
for (int j = ; j <= M ; j++) {
if (G[i][j] == '') continue;
int cnt1 = ,cnt2 = ;
for (int p = ; p <= N ; p++) if (G[p][j] == '') cnt1++;
for (int q = ; q <= M ; q++) if (G[i][q] == '') cnt2++;
init();
for (int p = ; p <= N ; p++) {
if (G[p][j] == '') continue;
for (int q = ; q <= M ; q++) {
if (G[i][q] == '' && G[p][q] == '')
add_edge(p,q,);
}
}
ans = max(ans,cnt1 + cnt2 - calcu());
}
}
printf("Case %d: %d\n", kase++, ans);
}
return ;
}

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

  1. [kuangbin带你飞]专题十 匹配问题

        A-L 二分匹配 M-O 二分图多重匹配 P-Q 二分图最大权匹配 R-S 一般图匹配带花树 模板请自己找     ID Origin Title   61 / 72 Problem A HD ...

  2. [kuangbin带你飞]专题十 匹配问题 二分匹配部分

    刚回到家 开了二分匹配专题 手握xyl模板 奋力写写写 终于写完了一群模板题 A hdu1045 对这个图进行 行列的重写 给每个位置赋予新的行列 使不能相互打到的位置 拥有不同的行与列 然后左行右列 ...

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

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

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

    layout: post title: 「kuangbin带你飞」专题十八 后缀数组 author: "luowentaoaa" catalog: true tags: - kua ...

  5. KUANGBIN带你飞

    KUANGBIN带你飞 全专题整理 https://www.cnblogs.com/slzk/articles/7402292.html 专题一 简单搜索 POJ 1321 棋盘问题    //201 ...

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

    Tarjan算法就不说了 想学看这 https://www.byvoid.com/blog/scc-tarjan/ https://www.byvoid.com/blog/biconnect/ 下面是 ...

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

    layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...

  8. 「kuangbin带你飞」专题十七 AC自动机

    layout: post title: 「kuangbin带你飞」专题十七 AC自动机 author: "luowentaoaa" catalog: true tags: - ku ...

  9. [kuangbin带你飞]专题1-23题目清单总结

    [kuangbin带你飞]专题1-23 专题一 简单搜索 POJ 1321 棋盘问题POJ 2251 Dungeon MasterPOJ 3278 Catch That CowPOJ 3279 Fli ...

随机推荐

  1. CSS3 : box-shadow

    box-shadow 用于设置边框阴影,语法: box-shadow: h-shadow v-shadow blur spread color inset; -moz-box-shadow: h-sh ...

  2. gcc options选项的优化及选择

    gcc options选项的优化 -c和-o都是gcc编译器的可选参数[options] -c表示只编译(compile)源文件但不链接,会把.c或.cc的c源程序编译成目标文件,一般是.o文件.[只 ...

  3. mongodb数据库高级操作

    1.创建索引 2.索引名称 3.其他索引 4.explain 5.操作索引 6.高级特性 7.固定集合 8.导入导出 9.上锁 10.添加用户 11.主从复制

  4. liniux备忘录-磁盘配额与进阶文件系统管理

    知识 磁盘配额Quota 可以限制磁盘的使用容量,可以对用户.群组磁盘的最大使用容量. 磁盘配额Quota的使用限制 只能针对整个文件系统. 核心必须支持Quota. 自行编译的核心需要注意 Quot ...

  5. Web.config配置文件中的属性add,key,value含义

    这是添加自定义字符串的方式,保存是以键-值的形式保存的,可以通过key获取value,一般用这种方法配置全局内使用的字符串. <configuration>是配置文件的根配置节. < ...

  6. hdu 3231 Box Relations (拓扑排序)

    Box Relations Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  7. CF115B Lawnmower

    题目描述 You have a garden consisting entirely of grass and weeds. Your garden is described by an n×mn×m ...

  8. LeetCode -- Merge Two Sorted Linked List

    Question: Merge two sorted linked lists and return it as a new list. The new list should be made by ...

  9. [Leetcode] distinct subsequences 不同子序列

    Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...

  10. 【NOIP模拟赛】公主的朋友 区间染色问题

    这道题大家都用的分块,然而我发现这是一个经典算法:区间染色问题. 我们区间染色时把区间分成若干连续的颜色段,然后我们每次染色删除原来的颜色段插入新的颜色段. 我们发现我们的时间复杂度直接与我们要染色区 ...