二分匹配:二分图的一些性质
二分图又称作二部图,是图论中的一种特殊模型。 设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 ;
}
跟上题类似。注意答案为什么是总数-匹配数/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提到了“带花树开花”算法来解非二分图的最大匹配。
http://builtinclz.abcz8.com/art/2012/ural1099.cpp
没错,这是用来解决URAL 1099 Work Schedule那题的。时间复杂度是O(N^3)
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带你飞 匹配问题 二分匹配 + 二分图多重匹配 + 二分图最大权匹配 + 一般图匹配带花树的更多相关文章
-
[kuangbin带你飞]专题十 匹配问题
A-L 二分匹配 M-O 二分图多重匹配 P-Q 二分图最大权匹配 R-S 一般图匹配带花树 模板请自己找 ID Origin Title 61 / 72 Problem A HD ...
-
[kuangbin带你飞]专题十 匹配问题 二分匹配部分
刚回到家 开了二分匹配专题 手握xyl模板 奋力写写写 终于写完了一群模板题 A hdu1045 对这个图进行 行列的重写 给每个位置赋予新的行列 使不能相互打到的位置 拥有不同的行与列 然后左行右列 ...
-
[kuangbin带你飞]专题十 匹配问题 二分图最大权匹配
二分图最大权匹配有km算法和网络流算法 km算法模板默认解决最大权匹配的问题 而使用最小费用最大流 是解决最小权匹配问题 这两种办法都可以求最大最小权 需要两次取反 TAT 感觉讲km会很难的样子.. ...
-
「kuangbin带你飞」专题十八 后缀数组
layout: post title: 「kuangbin带你飞」专题十八 后缀数组 author: "luowentaoaa" catalog: true tags: - kua ...
-
KUANGBIN带你飞
KUANGBIN带你飞 全专题整理 https://www.cnblogs.com/slzk/articles/7402292.html 专题一 简单搜索 POJ 1321 棋盘问题 //201 ...
-
Tarjan 联通图 Kuangbin 带你飞 联通图题目及部分联通图题目
Tarjan算法就不说了 想学看这 https://www.byvoid.com/blog/scc-tarjan/ https://www.byvoid.com/blog/biconnect/ 下面是 ...
-
「kuangbin带你飞」专题二十二 区间DP
layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...
-
「kuangbin带你飞」专题十七 AC自动机
layout: post title: 「kuangbin带你飞」专题十七 AC自动机 author: "luowentaoaa" catalog: true tags: - ku ...
-
[kuangbin带你飞]专题1-23题目清单总结
[kuangbin带你飞]专题1-23 专题一 简单搜索 POJ 1321 棋盘问题POJ 2251 Dungeon MasterPOJ 3278 Catch That CowPOJ 3279 Fli ...
随机推荐
-
java 27 - 4 反射之 通过反射获取成员变量并使用
类Field: 提供有关类或接口的单个字段的信息,以及对它的动态访问权限. A:获得类的成员变量 数组: 1.getFields(公共类的) 2.getDeclaredFields(所有类型的) B: ...
-
精通CSS :nth-child伪类
:nth-child 基本用法 :nth-child:nth-child(8) 选中第8个子元素 li:nth-child(8) span { background-color: #298EB2; b ...
-
easyui问题小记
在easyui1.4.3版本中,(其他版本不知道是不是也是这样的),绑定在datagridview上面的数据最好不要是带有特殊符号的字段,不然会导致部分的属性不好用,比如这样的字段 START_DA ...
-
VISUAL SVN安装 及客户端使用
1.为什么要用VisualSVN Server,而不用Subversion? 回答: 因为如果直接使用Subversion,那么在Windows 系统上,要想让它随系统启动,就要封装SVN Serve ...
-
201521123022 《Java程序设计》 第五周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过? ...
-
webpack学习(六)打包压缩js和css
打包压缩js与css 由于webpack本身集成了UglifyJS插件(webpack.optimize.UglifyJsPlugin)来完成对JS与CSS的压缩混淆,无需引用额外的插件, 其命令we ...
-
Openstack的视频学习
0.安装环境准备 部署架构: 网络模式(红色Net0为管理网络,Net1接外网,Net2是接虚拟机网络流量的): 虚拟化平台为VirtualBox,虚拟网络Host-Only网络的配置: Net0:管 ...
-
转 可能是最漂亮的Spring事务管理
Snailclimb 2018年05月21日阅读 4246 可能是最漂亮的Spring事务管理详解 Java面试通关手册(Java学习指南):github.com/Snailclimb/… 微信阅读地 ...
-
Java之IO(七)ObjectInputStream和ObjectOutputStream
转载请注明源出处:http://www.cnblogs.com/lighten/p/7003536.html 1.前言 本章介绍Java字节流中重要的成员,对象流ObjectInputStream和O ...
-
Java常用的输出调试技巧
--------siwuxie095 Eclipse 开发中常用的输出调试技巧: 先在左侧的 Package Explorer,右键->New->J ...