poj 2446 二分图最大匹配

题意:给一张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 }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值