2要素即:每条边有2个端点,两者至少选择一个。求最小选择的端点数。
对于这一题:
我们发现:每个泥泞格子要么被列木板覆盖,要么被横向模板覆盖,且至少选择一种类型的木板。
显然这符合2要素,直接转化为二分图模型来做:
我们对每一个泥泞的格子视为一条边,连接某一列木板,和某一行木板。
注意,第i行可能有多个木板,我们要赋予他们不同的id。
比如:
.**...**. 这一行
前面的2个泥泞格子与后面的泥泞格子所需要的行木板是不相同的。
预处理出id,直接二分图最大匹配即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e4+7;
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
char s[100][100];
int n,m;
int gt(int x,int y)
{
return (x-1)*n+y;
}
int r[100][100];
int c[100][100];
int vs[M],match[M];
bool dfs(int x)
{
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to;
if(vs[y])continue;
vs[y]=1;
if(!match[y]||dfs(match[y]))
{
match[y]=x;
return true;
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>(s[i]+1);
int sz=0,ln=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(s[i][j]=='*')
{
if(s[i][j-1]!='*')r[i][j]=++sz;
else r[i][j]=sz;
}
}
ln=sz;//左部图的数量,即横向板子的数量
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(s[j][i]=='*')
{
if(s[j-1][i]!='*')c[j][i]=++sz;
else c[j][i]=sz;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(s[i][j]=='*')
{
add(r[i][j],c[i][j]);
add(c[i][j],r[i][j]);
}
}
int ans=0;
// cout<<sz<<" "<<ln<<endl;
//横向板和纵向板为点,每个泥泞为边,求最小点覆盖。
for(int i=1;i<=ln;i++)
{
memset(vs,0,sizeof(vs));
if(dfs(i))ans++;
}
cout<<ans<<endl;
return 0;
}