[BZOJ4554][JZOJ4612] 【TJOI&HEOI2016】D2T1 游戏

Description

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

Solution

这种题实在是太经典了。

直接行列处理联通块,相交的联通块连边,跑二分图匹配即可。

但是我比赛的时候用了一种非常神奇的方法,因为我没有意识到这块只有一个也算块,
所以我对于每个块,把只有单个空地的都合并到所在的另一方向的块中,最后再特判从这个块中连出一个(相当于把这些块压缩成了一个)。

并且我并没有意识到行和列就是不同颜色,还打了个DFS染色~
于是整整打了2.7K

Code

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note
{
    int x,y;
}a[30005];
bool cmp(note x,note y)
{
    return x.x<y.x;
}
int cl[2505],n,m,l[2505],r[2505],map[55][55],a1[2505][2],pt[55][55],num,rd,bz[55][55],lm,rm,fd[2505];
bool b[2505];
void build(int x,int y)
{
    a[++rd].x=x;
    a[rd].y=y;
    a[++rd].x=y;
    a[rd].y=x;
}
void lrt(int i,int j,int v)
{
    int k=j;
    bool bz1=0;
    if (k+v<0||k+v>m||map[i][k+v]==2) 
    {
        num--;
        return;
    }
    while (k>0&&k<=m&&map[i][k]!=2) 
    {
        if (pt[i][k]&&!map[i][k]) build(pt[i][k],num);
        if (pt[i][k]) bz[i][k]=-1;
        if (!pt[i][k]) pt[i][k]=num,bz[i][k]=1;
        if (map[i][k]!=1)bz1=1; 
        k+=v;
    }
    if(!bz1) num--;
}
void udn(int i,int j,int v,int p1)
{
    int k=i;
    bool bz1=0;
    if ((p1==0)&&(k+v<0||k+v>n||map[k+v][j]==2)) 
    {
        num--;
        return;
    }
    while (k>0&&k<=n&&map[k][j]!=2) 
    {
        if (pt[k][j]&&!map[k][j]) build(pt[k][j],num),bz[k][j]=-1;
        if (!pt[k][j]) pt[k][j]=num,bz[k][j]=2,bz1=1;
        if (map[k][j]!=1)bz1=1;
        k+=v;
    }
    if (bz1==0) num--;
}
void color(int k,int v)
{
    int i;
    cl[k]=v;
    if (a1[k][0]!=0) 
    fo(i,a1[k][0],a1[k][1])
    {
        int p=a[i].y;
        if (cl[p]==-1) color(p,1-v); 
    }
}
int hungry(int k)
{
    int i;
    if (a1[k][0]!=0)
    fo(i,a1[k][0],a1[k][1])
    {
        int p=a[i].y;
        if (!b[p])
        {
            b[p]=1;
            if (!fd[p]||hungry(fd[p])) 
            {
                fd[p]=k;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout); 
    cin>>n>>m;
    int i,j,k;
    scanf("\n");
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            char ch;
            scanf("%c",&ch);
            if (ch=='#') map[i][j]=2;
            else if (ch=='*') map[i][j]=0;
            else map[i][j]=1;
        }
        scanf("\n");
    } 
    num=rd=0;
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            if (bz[i][j]!=-1&&map[i][j]<2)
            {
                int v=num,v1=0;
                if (bz[i][j]!=1) num++,lrt(i,j,1);
                if (num==v) v1=1;
                if (bz[i][j]!=2&&bz[i][j]!=-1) num++,udn(i,j,1,v1);
            }
        } 
    }
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            if ((map[i][j]==0)&&(bz[i][j]==1||bz[i][j]==2)) build(pt[i][j],++num);
        }
    }
    sort(a+1,a+rd+1,cmp);
    fo(i,1,rd)
    {
        if (a[i].x!=a[i-1].x) 
        {
            a1[a[i].x][0]=i;
            a1[a[i-1].x][1]=i-1;
        }
    }
    a1[a[rd].x][1]=rd;
    memset(cl,255,sizeof(cl)); 
    fo(i,1,num) if (cl[i]==-1) color(i,0);
    lm=rm=0;
    fo(i,1,num)
    {
        if (cl[i]==0) l[++lm]=i;
        else if(cl[i]==1)r[++rm]=i;
    }
    int ans=0;
    fo(i,1,lm)
    {
        memset(b,0,sizeof(b));
        ans+=hungry(l[i]);
    }
    cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值