洛谷:P6062 [USACO05JAN]Muddy Fields G

题目链接:P6062 [USACO05JAN]Muddy Fields G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

此题重点在二分图的建图。

考虑放置木板的决策,由于可以重复覆盖,所以对于两个可以选择的区间 a,b,若 b 被 a 覆盖,那么选择 a 一定优于选择 b。

解释如图:泥地为黄色,草地为绿色

显然,木板 A 一定比木板 B 更优,因为橙色部分可以重复覆盖。

由此易得所有的木板两头都是没有泥地的:若有,则增长此木板一定比原方案更优。

得到这个结论,我们可以先预处理出所有可能放置木板的位置,也就是在每行每列连续的泥地的位置。如图所示:所有木板可能放置的位置是下图的十个。

        

对于每一个点,要么被横着覆盖,要么被竖着覆盖。举例来讲,(3,3)在行上会被横木板 3 覆盖,在列上会被竖木板 4 覆盖。而我们要让所有的点都被覆盖,也就是需要满足下图所示的所有条件:

        

我们需要选出最少的木板满足这样的若干个约束条件。我们把每一个泥地看成一条边,左端点为其对应的横木板,右端点为其对应的竖木板。 显然这是一个二分图,左边全部为横木板,右边全部为竖木板。

        

考虑我们放置一个木板,相当于满足所有包含它的约束条件,也就是将其所连的边进行染色。 而对于每一个泥地(也就是边),如果它被染色,就证明这个格子的约束条件已被满足

所以这个问题转化为:一次操作可以选择一个点,将所连的边染色,目的是让所有边被染色。

即为二分图最小点覆盖。

而最小点覆盖等于最大匹配,故建图跑匈牙利即可。

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int N = 55 * 55;

int n, m;
bool st[N];
int match[N];
bool con[N][N];
char str[N][N];
int row[N][N], col[N][N];
int row_cnt, col_cnt;

bool find(int a)
{
    for (int b = 1; b <= col_cnt; b ++ )
    {
        if (!con[a][b]) continue;
        if (st[b]) continue;
        st[b] = true;
        
        if (match[b] == -1 || find(match[b]))
        {
            match[b] = a;
            return true;
        }
    }
    
    return false;
}

int main()
{
    cin >> n >> m;
    memset(match, -1, sizeof match);
    for (int i = 1; i <= n; i ++ ) cin >> str[i] + 1;
    
    for (int i = 1; i <= n; i ++ )//按行枚举找木板数
        for (int j = 1; j <= m; j ++ )
            if (str[i][j] == '*')
                if (j == 1 || str[i][j - 1] != '*') row[i][j] = ++ row_cnt ;
                else row[i][j] = row_cnt;
    
    for (int i = 1; i <= m; i ++ )//按列枚举找木板数
        for (int j = 1; j <= n; j ++ )
            if (str[j][i] == '*')
                if (j == 1 || str[j - 1][i] != '*') col[j][i] = ++ col_cnt;
                else col[j][i] = col_cnt;
    
    for (int i = 1; i <= n; i ++ )//建图
        for (int j = 1; j <= m; j ++ ) con[row[i][j]][col[i][j]] = true;
    
    int res = 0;
    for (int i = 1; i <= row_cnt; i ++ )//最小点覆盖 = 最大匹配数
    {
        memset(st, false, sizeof st);
        if (find(i)) res ++ ;
    }
    
    cout << res << endl;
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥也不会hh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值