【匈牙利】HDU5093[Battle ships]题解

题目概述

一张网格图,每个格子是海,浮冰或冰山,一只船(只占一个格子)只能放在海上。两只船放在同行同列需要满足他们之间至少有一座冰山。求最多能放多少船。

解题报告

一行一列只能放一个,一下就想到二分图最大匹配了,但是有障碍物,怎么办?其实问题的本质还是一样的,只不过我们需要这么处理:
这里写图片描述
由于出现了障碍物,“一行”和“一列”已经不能是原来意义上的一行和一列了。在同一行,如果被障碍物隔开,其实应该视作“多行”。也就是说我们可以把每个格子标两个号:“行”号和“列”号,障碍和浮冰处没有标号。在一行(一列)中,如果被障碍物隔开,就需要将之后的格子的标号+1,但被浮冰隔开则没有影响。这么处理之后,直接刷二分图最大匹配就行了。

示例程序

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=50,maxm=50,maxt=maxn*maxm;

int te,n,m,sumx,sumy,ans;
char pic[maxn+5][maxm+5];
int idx[maxn+5][maxm+5],idy[maxn+5][maxm+5];
int E,lnk[maxt/2+5],who[maxt/2+5],son[maxt+5],nxt[maxt+5];
bool vis[maxt/2+5];

char getrch() {char ch=getchar();while (ch!='*'&&ch!='o'&&ch!='#') ch=getchar();return ch;}
void make_x() //为行标号
{
    sumx=0;memset(idx,0,sizeof(idx));
    for (int i=1,j=1;i<=n;i++,j=1)
        while (j<=m)
            {
                for (;j<=m&&pic[i][j]=='#';j++);sumx+=j<=m;
                for (;j<=m&&pic[i][j]!='#';j++)
                    if (pic[i][j]=='*') idx[i][j]=sumx;
            }
}
void make_y() //为列标号
{
    sumy=0;memset(idy,0,sizeof(idy));
    for (int j=1,i=1;j<=m;j++,i=1)
        while (i<=n)
        {
            for (;i<=n&&pic[i][j]=='#';i++);sumy+=i<=n;
            for (;i<=n&&pic[i][j]!='#';i++)
                if (pic[i][j]=='*') idy[i][j]=sumy;
        }
}
void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[x];lnk[x]=E;}
bool Find(int x)
{
    if (vis[x]) return false;vis[x]=true;
    for (int j=lnk[x];j;j=nxt[j])
        if (!who[son[j]]||Find(who[son[j]]))
        {
            who[son[j]]=x;
            return true;
        }
    return false;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d",&te);
    while (te--)
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            pic[i][j]=getrch();
        make_x();make_y();
        E=0;memset(lnk,0,sizeof(lnk));
        for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            if (idx[i][j]&&idy[i][j]) //i,j放船意味着idx[i][j]->idy[i][j]
                Add(idx[i][j],idy[i][j]);
        ans=0;memset(who,0,sizeof(who));
        for (int i=1;i<=sumx;i++) //最大匹配
            memset(vis,0,sizeof(vis)),ans+=Find(i);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值