最小覆盖:选择尽量少的点,使得每条边至少有一个端点被选中;最小覆盖 = 最大匹配数;
poj3041最小覆盖;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<map>
#define ll long long
#define MAX 1000
#define INF INT_MAX
#define eps 1e-8
using namespace std;
int Left[MAX],n;
bool T[MAX];
vector<int>G[MAX];
bool match(int u){
for (int i = 0; i<G[u].size(); i++) if (!T[G[u][i]]){
int v = G[u][i];
T[v] = true;
if (!Left[v] || match(Left[v])){
Left[v] = u;
return true;
}
}
return false;
}
int mark[MAX];
int KM(){
int cnt = 0;
memset(Left,0,sizeof(Left));
for (int i = 1; i<=n; i++) if (mark[i]){
memset(T,0,sizeof(T));
if (match(i)) cnt++;
}
return cnt;
}
int main(){
int m;
while (scanf("%d%d",&n,&m) != EOF){
memset(mark,0,sizeof(mark));
for (int i = 0; i<=n; i++) G[i].clear();
int u,v;
for (int i = 1; i<=m; i++){
scanf("%d%d",&u,&v);
G[u].push_back(v);
mark[u] = 1;
}
printf("%d\n",KM());
}
return 0;
}
最小边覆盖:用最少的边覆盖所有的点;最小边覆盖 = 节点数 - 最大匹配数;
poj3020最小边覆盖;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<string>
#define ll long long
#define MAX 500
#define INF INT_MAX
#define eps 1e-8
using namespace std;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
char s[MAX][MAX];
int Left[MAX],n;
bool T[MAX];
vector<int>G[MAX];
bool match(int u){
for (int i = 0; i<G[u].size(); i++) if (!T[G[u][i]]){
int v = G[u][i];
T[v] = true;
if (!Left[v] || match(Left[v])){
Left[v] = u;
return true;
}
}
return false;
}
int mark[MAX];
int KM(){
int cnt = 0;
memset(Left,0,sizeof(Left));
for (int i = 0; i<=n; i++) if (mark[i]){
memset(T,0,sizeof(T));
if (match(i)) cnt++;
}
return cnt;
}
int main(){
int cas,x,y;
scanf("%d",&cas);
while (cas--){
memset(mark,0,sizeof(mark));
int cnt = 0;
scanf("%d%d",&x,&y);
n = x*y;
for (int i=0; i<=n; i++) G[i].clear();
for (int i = 1; i<=x; i++) scanf("%s",s[i]+1);
for (int i = 1; i<=x; i++){
for (int j = 1; j<=strlen(s[i]+1); j++ ) if (s[i][j] == '*'){
mark[(i-1)*y + j] = 1;
cnt++;
for (int k = 0; k<4; k++){
int ex = i + dx[k];
int ey = j + dy[k];
if (ex >= 1 && ex <= x && ey >=1 && ey <= y && s[ex][ey] == '*') G[(i-1)*y + j].push_back((ex-1)*y + ey);
}
}
}
printf("%d\n",cnt - KM()/2);
}
return 0;
}
最大独立子集:对于没一条边,至少有一个点不被选中;最大独立子集 = 节点数 - 最大匹配数;
注意:最大独立子集,常常需要去从对立面考虑,即可能需要将没有关系的节点连一条边,然后求最大独立子集,即为结果
poj3692 最大独立子集;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<stack>
#define ll long long
#define MAX 1000
#define eps 1e-8
#define INF INT_MAX
using namespace std;
int w[MAX][MAX],Left[MAX],n,m; //Left[i]记录与右边的节点i所匹配的左边的节点 (可以根据Left[]找出一组最大匹配);n、m分别表示左右节点的个数
bool S[MAX],T[MAX]; //记录右边的节点是否被访问过
bool match(int u){
S[u] = true; //从左边的节点u出发找增广路
for (int i = 1; i<=m; i++) if (!T[i] && w[u][i]){
T[i] = true;
if (!Left[i] || match(Left[i])){
Left[i] = u;
return true;
}
}
return false;
}
int KM(){
int cnt = 0;
memset(Left,0,sizeof(Left));
for (int i=1; i<=n; i++){
memset(T,0,sizeof(T));
memset(S,0,sizeof(S));
if (match(i)) cnt++; //如果找到增广路,则说明匹配数目增加1
}
return cnt;
}
int mark[MAX][MAX];
int main(){
int M,cas = 0;
while (scanf("%d%d%d",&n,&m,&M) && !(n == 0 && m == 0 && M == 0)){
cas++;
int u,v;
memset(mark,0,sizeof(mark));
memset(w,0,sizeof(w));
for (int i = 1; i<=M; i++){
scanf("%d%d",&u,&v);
mark[u][v] = 1;
}
for (int i = 1; i<=n; i++){
for (int j = 1; j <= m; j++)
if (!mark[i][j]) w[i][j] = 1;
}
int ans = KM();
// for (int i = 1; i<=n; i++) if (S[i]) printf("%d ",i); printf("\n");
// for (int i = 1; i<=m; i++) if (T[i]) printf("%d ",i); printf("\n");
// for (int i = 1; i<=m; i++) printf("%d ",Left[i]); printf("\n");
printf("Case %d: %d\n",cas,n+m - ans);
}
return 0;
}
DAG最小路径覆盖:在图中找尽量少的路径,使得每个节点恰好在一条路径上;
解法:把所有节点拆违X节点i,和Y节点i',如果原图中有边i->j,则而二分图中引入边i->j',则节点数(原图中节点数n) - 最大覆盖数 = 最小路径覆盖的路径数;
poj2060最小路径覆盖;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<string>
#include<stack>
#include<map>
#define ll long long
#define MAX 1000
#define INF INT_MAX
#define eps 1e-8
using namespace std;
using namespace std;
int Left[MAX],n;
bool T[MAX];
vector<int>G[MAX];
bool match(int u){
for (int i = 0; i<G[u].size(); i++) if (!T[G[u][i]]){
int v = G[u][i];
T[v] = true;
if (!Left[v] || match(Left[v])){
Left[v] = u;
return true;
}
}
return false;
}
int KM(){
int cnt = 0;
memset(Left,0,sizeof(Left));
for (int i = 0; i<n; i++){
memset(T,0,sizeof(T));
if (match(i)) cnt++;
}
return cnt;
}
struct Node{
int time,a,b,c,d;
}p[MAX];
bool judge(int i, int j){
int end = p[i].time + abs(p[i].a- p[i].c) + abs(p[i].b - p[i].d) + abs(p[i].c - p[j].a) + abs(p[i].d - p[j].b) + 1;
if (end <= p[j].time) return true;
return false;
}
int main(){
int cas;
scanf("%d",&cas);
while (cas--){
scanf("%d",&n);
int u,v;
for (int i = 0; i<=n; i++) G[i].clear();
for (int i = 1; i<=n; i++){
scanf("%d:%d",&u,&v);
p[i].time = u*60 + v;
scanf("%d%d%d%d",&p[i].a,&p[i].b,&p[i].c,&p[i].d);
}
for (int i = 1; i<=n; i++){
for (int j = 1; j<=n; j++){
if (judge(i,j)) G[i].push_back(j);
}
}
printf("%d\n",n - KM());
}
return 0;
}
注意:在用二分图最大匹配模型来解决问题是,建模是关键;图中没有天然的二分图时,常需要将每个点拆为X节点,和Y节点,然后建图,这样求的的最大匹配数为原图中的最大匹配数的2倍(相当于每条边被算了两次)。