正题
大意
一个n*m的棋盘上有k个洞,将1*2的木条放在上面,不能铺在洞上,不能重叠,求能不能铺满整个棋盘。
解题思路
用点来建立二分图,然后求最大匹配。
但是奇偶建图会快两倍。奇偶建图就是相邻的块可以相连接,所以它们可以不放在同一边。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct line{int to,next;
}le[11001];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int tot,w,s,n,m,k,num[41][41],link[1101],xx,y,one,two,ls[1101];
bool cover[1101],a[41][41];
void add(int x,int y)
{le[++tot].next=ls[x];le[tot].to=y;ls[x]=tot;
}//邻接表
bool find(int x)//最大匹配
{int p;for (int q=ls[x];q;q=le[q].next){if (!cover[le[q].to]){cover[le[q].to]=true;p=link[le[q].to];link[le[q].to]=x;if (!p || find(p)) return true;link[le[q].to]=p;}}return false;
}
int main()
{scanf("%d%d%d",&m,&n,&k);w=k;for (int i=1;i<=k;i++){scanf("%d%d",&xx,&y);a[xx][y]=true;}if((n*m-k)&1) return puts("NO")&1;//特判节省时间for (int i=1;i<=n;i++)for (int j=1;j<=m;j++)if (!a[i][j]){if ((i+j)%2==1) num[i][j]=++one;else num[i][j]=++two;//分为奇偶标号}for (int i=1;i<=n;i++)for (int j=1;j<=m;j++){if (!a[i][j] && (i+j)%2==1)for (int k=0;k<4;k++){if (i+dx[k]>=1 && i+dx[k]<=n && j+dy[k]>=1 && j+dy[k]<=m)if (!a[i+dx[k]][j+dy[k]])add(num[i][j],num[i+dx[k]][j+dy[k]]);//建立连接}} for (int i=1;i<=one;i++){memset(cover,0,sizeof(cover));if (find(i)) s++;}if (n*m==s*2+w) printf("YES");//计算答案else printf("NO");
}