BZOJ 4554 游戏 HEOI2016 二分图匹配

Problem

Problem Description

在2016年,佳缘姐姐喜欢上了一款游戏,叫做泡泡堂。简单的说,这个游戏就是在一张地图上放上若干个炸弹,看是否能炸到对手,或者躲开对手的炸弹。在玩游戏的过程中,小H想到了这样一个问题:当给定一张地图,在这张地图上最多能放上多少个炸弹能使得任意两个炸弹之间不会互相炸到。炸弹能炸到的范围是该炸弹所在的一行和一列,炸弹的威力可以穿透软石头,但是不能穿透硬石头。给定一张n×m的网格地图:其中*代表空地,炸弹的威力可以穿透,可以在空地上放置一枚炸弹。x代表软石头,炸弹的威力可以穿透,不能在此放置炸弹。#代表硬石头,炸弹的威力是不能穿透的,不能在此放置炸弹。例如:给出1×4的网格地图 xx ∗ x x ∗ ,这个地图上最多只能放置一个炸弹。给出另一个1×4的网格地图*x #* *x #* ,这个地图最多能放置两个炸弹。现在小H任意给出一张n×m的网格地图,问你最多能放置多少炸弹。

Input

第一行输入两个正整数n,m,n表示地图的行数,m表示地图的列数。1≤n,m≤50。接下来输入n行m列个字符,代表网格地图。*的个数不超过n×m个。

Output

输出一个整数a,表示最多能放置炸弹的个数

Sample Input

4 4
#∗∗∗
∗#∗∗
∗∗#∗
xxx#

Sample Output

5

Solution

刚开始看的时候完全没有思路,完全看不出来应该用什么算法。发现原来学了那么多算法,数据结构,结果到头来还是只会打搜索。但其实这道题是十分巧妙的,它用了十分巧妙的伪装,用地图来伪装,因为很多地图有关的题,都是与搜索有关的题目。然后T成狗。
正解十分巧妙,将地图转化为二分图,其需要匹配的为地图的每一行、列中的联通块。在两块硬石头之间就是一个联通块,因为若把炸弹放在这个联通块上任一地方,都会使得这个联通块无法再放炸弹,且炸弹的威力对行、列均有影响,所以就相当于二分图匹配。对行列的联通块分别进行编号,对于地图上的任意一个map[i][j]=’*’,都要对相应的两个联通块连边,最后的答案就是这个二分图的最大匹配。
吐槽:这个题目的输入数据可能有bug,在读入地图的时候,一定要写get()函数,是期望的字符才读。因为这个,爆零了好几次。

Code

#include 
#include 
#include 
using namespace std;
struct data{
    int v,nxt;
}edge[3000];
int n,m,p,ans,htot,ltot,h[55][55],l[55][55],head[2510],f[2510];
char map[55][55];
bool used[2510];
char get()
{
    char ch=getchar();
    while(ch!='*'&&ch!='#'&&ch!='x') ch=getchar();
    return ch;
}
inline void insert(int u,int v)
{
    edge[++p].v=v;
    edge[p].nxt=head[u];
    head[u]=p;
}
void input()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        map[i][j]=get();
}
void init()
{
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        if(map[i][j]!='#')
        {
            if(j==1||map[i][j-1]=='#')
              htot++;
            h[i][j]=htot;
        }
    for(int i=1;i<=m;i++)
      for(int j=1;j<=n;j++)
        if(map[j][i]!='#')
        {
            if(j==1||map[j-1][i]=='#')
              ltot++;
            l[j][i]=ltot;
        }
}
bool check(int k)
{
    for(int i=head[k];i;i=edge[i].nxt)
      if(!used[edge[i].v])
      {
        used[edge[i].v]=true;
        if(!f[edge[i].v]||check(f[edge[i].v]))
        {
            f[edge[i].v]=k;
            return true;
        }
      }
    return false;
}
int main()
{
    input();
    init();
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        if(map[i][j]=='*')
          insert(h[i][j],l[i][j]);
    for(int i=1;i<=htot;i++)
    {
        memset(used,0,sizeof(used));
        if(check(i))
          ans++;
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(好题集,网络流/费用流,BZOJ)