题解 P6062 【[USACO05JAN]Muddy Fields G】
Miko35
2020-10-05 14:27:15
此题重点在二分图的建图。
考虑放置木板的决策,由于可以重复覆盖,所以对于两个可以选择的区间 $a,b$,若 $b$ 被 $a$ 覆盖,那么选择 $a$ 一定优于选择 $b$。
解释如图:泥地为黄色,草地为绿色
![](https://i.loli.net/2020/10/05/6Xcy1QAImGVW7qh.png)
显然,木板 $A$ 一定比木板 $B$ 更优,因为橙色部分可以重复覆盖。
由此易得所有的木板两头都是没有泥地的:若有,则增长此木板一定比原方案更优。
得到这个结论,我们可以先预处理出所有可能放置木板的位置,也就是在每行每列连续的泥地的位置。如图所示:所有木板可能放置的位置是下图的十个。
![](https://i.loli.net/2020/10/05/2Kz3QrDuPWJlqvd.png)
对于每一个点,要么被横着覆盖,要么被竖着覆盖。举例来讲,$(3,3)$ 在行上会被横木板 $3$ 覆盖,在列上会被竖木板 $4$ 覆盖。而我们要让所有的点都被覆盖,也就是需要满足下图所示的所有条件:
![](https://i.loli.net/2020/10/05/xJb7FqRIX6POMgw.png)
我们需要选出最少的木板满足这样的若干个约束条件。**我们把每一个泥地看成一条边,左端点为其对应的横木板,右端点为其对应的竖木板。** 显然这是一个二分图,左边全部为横木板,右边全部为竖木板。
![](https://i.loli.net/2020/10/05/p69WzRj7N8fJMOs.png)
考虑我们**放置一个木板**,相当于满足所有包含它的约束条件,**也就是将其所连的边进行染色**。 而对于每一个泥地(也就是边),**如果它被染色,就证明这个格子的约束条件已被满足**。
所以这个问题转化为:**一次操作可以选择一个点,将所连的边染色,目的是让所有边被染色。**
即为二分图最小点覆盖。
而最小点覆盖等于最大匹配,故建图跑匈牙利即可。
代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
int n,m;
int cnt1,cnt2;
bool a[1002][1002];
int match[1002];
int ans;
bool visit[1002];
bool dfs(int x){
for(int i=1;i<=cnt2;i++){
if(a[x][i]&&!visit[i]){
visit[i]=1;
if(!match[i]||dfs(match[i])){
match[i]=x;
return 1;
}
}
}
return 0;
}
char ch[1002][1002];
int h[1002][1002];
int c[1002][1002];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){//统计横向木板
cin>>ch[i]+1;
for(int j=1;j<=m;j++){
if(ch[i][j]=='*'){
if(j==1||ch[i][j-1]!='*'){
h[i][j]=++cnt1;
}
else h[i][j]=cnt1;
}
}
}
for(int i=1;i<=m;i++){//统计竖向木板
for(int j=1;j<=n;j++){
if(ch[j][i]=='*'){
if(j==1||ch[j-1][i]!='*'){
c[j][i]=++cnt2;
}
else c[j][i]=cnt2;
a[h[j][i]][c[j][i]]=1;
}
}
}
for(int i=1;i<=cnt1;i++){//跑匈牙利
memset(visit,0,sizeof visit);
ans+=dfs(i);
}
cout<<ans;
return 0;
}
```