题意:给一张m*n的棋盘,上面有k个格子有洞,用1*2的骨牌将棋盘覆盖,每个格子必须被恰好覆盖一次,有洞的地方不能覆盖,问是否存在这样的方案。
思路:容易想到将格点作为二分图中的点,骨牌作为二分图中的边(骨牌可能放置时),求二分图的最大匹配,如果匹配数与n*m-k相等,则方案存在,否则不存在;容易想到当n*m-k为奇数时方案一定不存在;
构思时卡在怎么将格点用一维坐标表示出来,后来发现是自己蠢了.....标号就可以解决问题。
坑点:读入洞的坐标时列号在前,行号在后。
不算坑点的坑点:题目有多组case而题面上没有写出来。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 6 const int maxn = 50; 7 bool g[maxn*maxn][maxn*maxn]; 8 int link[maxn*maxn]; 9 bool check[maxn*maxn]; 10 int maze[maxn][maxn]; 11 12 int n,m,cnt; 13 int dir[4][2] = {{1,0},{-1,0},{0,-1},{0,1}}; 14 15 bool dfs(int u){ 16 for(int i = 1; i <= cnt; ++i){ 17 if(!check[i] && g[u][i]){ 18 check[i] = true; 19 if(link[i] == -1 || dfs(link[i])){ 20 link[i] = u; 21 return true; 22 } 23 } 24 } 25 return false; 26 } 27 28 int hungarian(){ 29 int ans = 0; 30 memset(link,-1,sizeof(link)); 31 for(int i = 1; i <= cnt; ++i){ 32 memset(check,0,sizeof(check)); 33 if(dfs(i)) ++ans; 34 } 35 return ans; 36 } 37 38 int main(){ 39 int k; 40 while(scanf("%d%d%d",&m,&n,&k) == 3){ 41 memset(maze,0,sizeof(maze)); 42 memset(g,0,sizeof(g)); 43 cnt = 0; 44 int x,y; 45 for(int p = 0; p < k; ++p){ 46 scanf("%d%d",&x,&y); 47 maze[y][x] = -1; 48 } 49 if((n*m-k) & 1){ 50 printf("NO\n"); 51 continue; 52 } 53 for(int i = 1; i <= m; ++i){ 54 for(int j = 1; j <= n; ++j){ 55 if(maze[i][j] == 0){ 56 maze[i][j] = ++cnt; 57 } 58 } 59 } 60 for(int i = 1; i <= m; ++i){ 61 for(int j = 1; j <= n; ++j){ 62 if(maze[i][j] > 0){ 63 for(int p = 0; p < 4; ++p){ 64 int nx = i + dir[p][0], ny = j + dir[p][1]; 65 if(nx >= 1 && nx <= m && ny >= 1 && ny <= n){ 66 g[maze[i][j]][maze[nx][ny]] = 1; 67 } 68 } 69 } 70 } 71 } 72 int res = hungarian(); 73 if(res + k == n*m) printf("YES\n"); 74 else printf("NO\n"); 75 } 76 return 0; 77 }