原文链接http://www.cnblogs.com/zhouzhendong/p/8254062.html
题目传送门 - HDU1507
题意概括
有一个n*m的棋盘,有些点是废的。
现在让你用1*2的矩形覆盖所有的不废的点,并且不重叠,问最多可以覆盖多少个1*2的矩形,输出方案,有SPJ。
输入描述:
多组数据,每组首先两个数n,m(如果n和m为0,则结束程序)
然后给出k
然后给出k个二元组(x,y)表示废点的坐标。
题解
按照前两片博文的算法已经不行了,因为方案不对了。
所以我们要进行黑白染色。
仅从(x,y)(x+y为奇数)向四连通方向连边,然后二分图匹配即可。
其中match数组的值就是匹配对象。
代码
#include <cstring> #include <algorithm> #include <cstdlib> #include <cmath> #include <cstdio> using namespace std; const int N=105,K=55; int dx[4]={ 0, 0,-1, 1}; int dy[4]={-1, 1, 0, 0}; int n,m,k,cnt,pl[N][N],tn[N][N],bj[K],x[K],y[K]; int g[K][K],vis[K],match[K]; bool check(int x,int y){ return 1<=x&&x<=n&&1<=y&&y<=m&&tn[x][y]; } bool Match(int x){ for (int i=1;i<=cnt;i++) if (!vis[i]&&g[x][i]){ vis[i]=1; if (!match[i]||Match(match[i])){ match[i]=x; return 1; } } return 0; } int hungary(){ int res=0; memset(match,0,sizeof match); for (int i=1;i<=cnt;i++){ if (!bj[i]) continue; memset(vis,0,sizeof vis); if (Match(i)) res++; } return res; } int main(){ while (~scanf("%d%d",&n,&m)&&(n||m)){ scanf("%d",&k); memset(pl,0,sizeof pl); for (int i=1,x,y;i<=k;i++) scanf("%d%d",&x,&y),pl[x][y]=1; memset(tn,0,sizeof tn); cnt=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (!pl[i][j]){ bj[tn[i][j]=++cnt]=(i+j)&1; x[cnt]=i,y[cnt]=j; } memset(g,0,sizeof g); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if ((i+j)&1) for (int d=0;d<4;d++){ int a=i+dx[d],b=j+dy[d]; if (check(a,b)) g[tn[i][j]][tn[a][b]]=1; } int ans=hungary(); printf("%d\n",ans); for (int i=1;i<=cnt;i++) if (match[i]) printf("(%d,%d)--(%d,%d)\n",x[i],y[i],x[match[i]],y[match[i]]); } return 0; }