与二分图不同,一般图的最大匹配用的是带花树开花算法。当然,本质都是寻找增广路。
下面的模板是来自这个博客的 http://fanhq666.blog.163.com/blog/static/8194342620120304463580/
ural1099(模板)
#include <cstdio> #include <algorithm> #include <vector> using namespace std; const int NMax=230; int Next[NMax]; int spouse[NMax]; int belong[NMax]; int findb(int a){ return belong[a]==a?a:belong[a]=findb(belong[a]); } void together(int a,int b){ a=findb(a),b=findb(b); if (a!=b)belong[a]=b; } vector<int> E[NMax]; int N; int Q[NMax],bot; int mark[NMax]; int visited[NMax]; int findLCA(int x,int y){ static int t=0; t++; while (1){ if (x!=-1){ x=findb(x); if (visited[x]==t)return x; visited[x]=t; if (spouse[x]!=-1)x=Next[spouse[x]]; else x=-1; } swap(x,y); } } void goup(int a,int p){ while (a!=p){ int b=spouse[a],c=Next[b]; if (findb(c)!=p)Next[c]=b; if (mark[b]==2)mark[Q[bot++]=b]=1; if (mark[c]==2)mark[Q[bot++]=c]=1; together(a,b); together(b,c); a=c; } } void findaugment(int s){ for (int i=0;i<N;i++)Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1; Q[0]=s;bot=1;mark[s]=1; for (int head=0;spouse[s]==-1 && head<bot;head++){ int x=Q[head]; for (int i=0;i<(int)E[x].size();i++){ int y=E[x][i]; if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){ if (mark[y]==1){ int p=findLCA(x,y); if (findb(x)!=p)Next[x]=y; if (findb(y)!=p)Next[y]=x; goup(x,p); goup(y,p); }else if (spouse[y]==-1){ Next[y]=x; for (int j=y;j!=-1;){ int k=Next[j]; int l=spouse[k]; spouse[j]=k;spouse[k]=j; j=l; } break; }else{ Next[y]=x; mark[Q[bot++]=spouse[y]]=1; mark[y]=2; } } } } } int Map[NMax][NMax]; int main() { scanf("%d",&N); int x,y; for (int i=0;i<N;i++)for (int j=0;j<N;j++)Map[i][j]=0; while (scanf("%d%d",&x,&y)!=EOF){ x--;y--; if (x!=y && !Map[x][y]){ Map[x][y]=Map[y][x]=1; E[x].push_back(y); E[y].push_back(x); } } for (int i=0;i<N;i++)spouse[i]=-1; for (int i=0;i<N;i++)if (spouse[i]==-1) findaugment(i); int ret=0; for (int i=0;i<N;i++)if (spouse[i]!=-1)ret++; printf("%d\n",ret); for (int i=0;i<N;i++) if (spouse[i]!=-1 && spouse[i]>i) printf("%d %d\n",i+1,spouse[i]+1); return 0; }
做了几道类模板的题目
题意:在一个棋盘上给定n的棋子的位子,轮流从棋盘上移除棋子,后者移动的棋子的与前者移动的棋子的哈密顿距离不能超过L。问后手能否赢。
分析:将哈密顿距离<= L 的棋子连边,很直观的发现,若存在完美匹配,则后手赢。
zoj3316
#include <cstdio> #include <algorithm> #include <vector> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <string.h> #include <math.h> using namespace std; const int N = 400; int Next[N]; int spouse[N]; int belong[N]; int findb(int a){ return belong[a] == a ? a : belong[a] = findb(belong[a]); } void together(int a,int b){ a=findb(a),b=findb(b); if (a!=b) belong[a]=b; } vector<int> E[N]; int Q[N],bot; int mark[N]; int visited[N]; int n; int findLCA(int x,int y){ static int t=0; t++; while (1){ if (x!=-1){ x=findb(x); if (visited[x]==t)return x; visited[x]=t; if (spouse[x]!=-1)x=Next[spouse[x]]; else x=-1; } swap(x,y); } } void goup(int a,int p){ while (a!=p){ int b=spouse[a],c=Next[b]; if (findb(c)!=p)Next[c]=b; if (mark[b]==2)mark[Q[bot++]=b]=1; if (mark[c]==2)mark[Q[bot++]=c]=1; together(a,b); together(b,c); a=c; } } void findaugment(int s){ for (int i=0;i<n;i++) Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1; Q[0]=s;bot=1;mark[s]=1; for (int head=0;spouse[s]==-1 && head<bot;head++){ int x=Q[head]; for (int i=0;i<(int)E[x].size();i++){ int y=E[x][i]; if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){ if (mark[y]==1){ int p=findLCA(x,y); if (findb(x)!=p)Next[x]=y; if (findb(y)!=p)Next[y]=x; goup(x,p); goup(y,p); }else if (spouse[y]==-1){ Next[y]=x; for (int j=y;j!=-1;){ int k=Next[j]; int l=spouse[k]; spouse[j]=k;spouse[k]=j; j=l; } break; }else{ Next[y]=x; mark[Q[bot++]=spouse[y]]=1; mark[y]=2; } } } } } pair<int, int> p[N]; int main() { int L; while(scanf("%d",&n) == 1) { for(int i = 0; i < n; ++i) scanf("%d %d",&p[i].first,&p[i].second); scanf("%d",&L); for(int i = 0; i < n; ++i) for(int j = i + 1; j < n; ++j) { int dis = abs(p[i].first - p[j].first) + abs(p[i].second - p[j].second); if(dis <= L) { E[i].push_back(j); E[j].push_back(i); } } for (int i=0;i<n;i++)spouse[i]=-1; for (int i=0;i<n;i++) if(spouse[i] == -1) findaugment(i); int ans = 0; for(int i = 0; i < n; ++i) if(spouse[i] != -1) ++ans; if(ans == n) puts("YES"); else puts("NO"); for(int i = 0; i < n; ++i) E[i].clear(); } return 0; }
题意:在一个R行C列的棋盘上,俩个人轮流移动一个棋子,每次可以向相邻的20个格子移动,走过的每个格子自能走一次。另外,某些各自一开始就固定了不能走。
无法移动者输。问:先手能否赢。
分析:首先,忽略K点,将其他能相互移动的格子连边,求一次最大匹配,再将K点加入图中,若存在增广路,则先手赢,否则后手赢。
hdu3446
#include <cstdio> #include <algorithm> #include <vector> #include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; const int N = 230; int Next[N]; int spouse[N]; int belong[N]; int findb(int a){ return belong[a] == a ? a : belong[a] = findb(belong[a]); } void together(int a,int b){ a=findb(a),b=findb(b); if (a!=b) belong[a]=b; } vector<int> E[N]; int Q[N],bot; int mark[N]; int visited[N]; int n; int findLCA(int x,int y){ static int t=0; t++; while (1){ if (x!=-1){ x=findb(x); if (visited[x]==t)return x; visited[x]=t; if (spouse[x]!=-1)x=Next[spouse[x]]; else x=-1; } swap(x,y); } } void goup(int a,int p){ while (a!=p){ int b=spouse[a],c=Next[b]; if (findb(c)!=p)Next[c]=b; if (mark[b]==2)mark[Q[bot++]=b]=1; if (mark[c]==2)mark[Q[bot++]=c]=1; together(a,b); together(b,c); a=c; } } void findaugment(int s){ for (int i=0;i<n;i++) Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1; Q[0]=s;bot=1;mark[s]=1; for (int head=0;spouse[s]==-1 && head<bot;head++){ int x=Q[head]; for (int i=0;i<(int)E[x].size();i++){ int y=E[x][i]; if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){ if (mark[y]==1){ int p=findLCA(x,y); if (findb(x)!=p)Next[x]=y; if (findb(y)!=p)Next[y]=x; goup(x,p); goup(y,p); }else if (spouse[y]==-1){ Next[y]=x; for (int j=y;j!=-1;){ int k=Next[j]; int l=spouse[k]; spouse[j]=k;spouse[k]=j; j=l; } break; }else{ Next[y]=x; mark[Q[bot++]=spouse[y]]=1; mark[y]=2; } } } } } int has[N][N]; int dir[21][2] = {{-2,-2},{-2,-1},{-2,1},{-2,2},{-1,-2},{-1,-1},{-1,0},{-1,1},{-1,2},{0,-1},{0,1},{1,-2},{1,-1},{1,0},{1,1},{1,2},{2,-2},{2,-1},{2,1},{2,2}}; char str[N][N]; bool Map[N][N]; int main() { int T, Ki, Kj, cas = 0; scanf("%d",&T); while(T--) { int r, c; scanf("%d %d",&r,&c); n = 0; for(int i = 0; i < r; ++i) { scanf("%s",str[i]); for(int j = 0; j < c; ++j) { if(str[i][j] == 'K') Ki = i, Kj = j; if(str[i][j] != '#') has[i][j] = n++; } } memset(Map, 0, sizeof(Map)); //cout << n << endl; for(int i = 0; i < r; ++i) for(int j = 0; j < c; ++j) { if(str[i][j] != '.') continue; for(int k = 0; k < 20; ++k) { int ni = i + dir[k][0]; int nj = j + dir[k][1]; if(ni < 0 || ni >= r || nj < 0 || nj >= c || str[ni][nj] != '.') continue; int a = has[i][j], b = has[ni][nj]; if(Map[a][b] == 0) { E[a].push_back(b); E[b].push_back(a); Map[a][b] = Map[b][a] = 1; } } } for (int i=0;i<n;i++)spouse[i]=-1; for (int i=0;i<n;i++)if (spouse[i]==-1) findaugment(i); for(int k = 0; k < 20; ++k) { int i = Ki + dir[k][0]; int j = Kj + dir[k][1]; if(i < 0 || i >= r || j < 0 || j >= c || str[i][j] != '.') continue; E[has[Ki][Kj]].push_back(has[i][j]); E[has[i][j]].push_back(has[Ki][Kj]); } findaugment(has[Ki][Kj]); printf("Case #%d: ", ++cas); if(spouse[has[Ki][Kj]] != -1) puts("daizhenyang win"); else puts("daizhenyang lose"); for(int i = 0; i < n; ++i) E[i].clear(); } return 0; }
题意:给定一个无向图,通过删边,问能否通过删边,得到一个得定的点的度数序列? 图中存在重边,不存在自环。
思路:参考自http://blog.sina.com.cn/s/blog_6af663940100n7pr.html
“我们应该删除哪些边呢? 预处理每个顶点的度数d[i], 若d[i] = deg[i], 那么 与这个点相连的边是不能删掉的。原因很显然。若i与j之间有边,并且d[i]>deg[i], d[j]>deg[j]那么这条边是可以删除的。接下来如何建图呢? 将每个点i 拆成 d[i] - deg[i]个点。如果i与j之间的边e可以删除, 则边e与i、j拆出的每个点连一条边 ei, ej(重边连多次)。然后求该一般图最大匹配,若存在完美匹配,则YES。”
一开始将一条可删边e的俩个端点各自拆出来的点,俩俩连了一条边,结果直接TLE了。 其实也是,连了很多多余的边。对于可删边e 的俩个端点i 和 j , 将i和j拆出的所有点连边,将j和i拆除的所有点连边即可。。 200+ms
hdu3551
#include <cstdio> #include <algorithm> #include <vector> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <string.h> #include <math.h> using namespace std; const int N = 800; int Next[N]; int spouse[N]; int belong[N]; int findb(int a){ return belong[a] == a ? a : belong[a] = findb(belong[a]); } void together(int a,int b){ a=findb(a),b=findb(b); if (a!=b) belong[a]=b; } vector<int> E[N]; int Q[N],bot; int mark[N]; int visited[N]; int n; int findLCA(int x,int y){ static int t=0; t++; while (1){ if (x!=-1){ x=findb(x); if (visited[x]==t)return x; visited[x]=t; if (spouse[x]!=-1)x=Next[spouse[x]]; else x=-1; } swap(x,y); } } void goup(int a,int p){ while (a!=p){ int b=spouse[a],c=Next[b]; if (findb(c)!=p)Next[c]=b; if (mark[b]==2)mark[Q[bot++]=b]=1; if (mark[c]==2)mark[Q[bot++]=c]=1; together(a,b); together(b,c); a=c; } } void findaugment(int s){ for (int i=0;i<n;i++) Next[i]=-1,belong[i]=i,mark[i]=0,visited[i]=-1; Q[0]=s;bot=1;mark[s]=1; for (int head=0;spouse[s]==-1 && head<bot;head++){ int x=Q[head]; for (int i=0;i<(int)E[x].size();i++){ int y=E[x][i]; if (spouse[x]!=y && findb(x)!=findb(y) && mark[y]!=2){ if (mark[y]==1){ int p=findLCA(x,y); if (findb(x)!=p)Next[x]=y; if (findb(y)!=p)Next[y]=x; goup(x,p); goup(y,p); }else if (spouse[y]==-1){ Next[y]=x; for (int j=y;j!=-1;){ int k=Next[j]; int l=spouse[k]; spouse[j]=k;spouse[k]=j; j=l; } break; }else{ Next[y]=x; mark[Q[bot++]=spouse[y]]=1; mark[y]=2; } } } } } pair<int, int> id[N]; int D[N]; int xx[N], yy[N]; int d[N]; void build(int n1, int m) { for(int i = 0; i <= N; ++i) id[i].first = -1; n = 0; for(int i = 0; i < m; ++i) { int u = xx[i], v = yy[i]; if(id[u].first == -1) { id[u] = make_pair(n, n + d[u] - D[u] - 1); n += (d[u] - D[u]); } if(id[v].first == -1) { id[v] = make_pair(n, n + d[v] - D[v] - 1); n += (d[v] - D[v]); } if(id[n1 + i + 1].first == -1) { id[n1 + i + 1] = make_pair(n, n + 1); n += 2; } int t = id[n1 + i + 1].first; E[t].push_back(t + 1); E[t + 1].push_back(t); for(int j = id[u].first; j <= id[u].second; ++j) E[t].push_back(j),E[j].push_back(t); for(int j = id[v].first; j <= id[v].second; ++j) E[t + 1].push_back(j),E[j].push_back(t + 1); } } int main() { int T, cas = 0; int n1, m; scanf("%d",&T); while(T--) { scanf("%d %d",&n1,&m); memset(d,0,sizeof(d)); for(int i = 0; i < m; ++i) { scanf("%d %d",&xx[i],&yy[i]); ++d[xx[i]]; ++d[yy[i]]; } for(int i = 1; i <= n1; ++i) scanf("%d",&D[i]);; bool flag = true; for(int i = 1; i <= n1; ++i) { if(d[i] < D[i]) { flag = false; break; } } printf("Case %d: ",++cas); if(!flag) { puts("NO"); continue; } build(n1, m); for (int i=0;i<n;i++)spouse[i]=-1; for (int i=0;i<n;i++) if(spouse[i] == -1) findaugment(i); int ans = 0; for(int i = 0; i < n; ++i) if(spouse[i] != -1) ++ans; if(ans == n) puts("YES"); else puts("NO"); for(int i = 0; i < n; ++i) E[i].clear(); } return 0; }