【二分图匹配/网络流】宫廷守卫

第1题     宫廷守卫(guards.pas)

 

【问题描述】

        从前有一个王国,这个王国的城堡是一个矩形,被分为M×N个方格。一些方格是墙,而另一些是空地。这个王国的国王在城堡里设了一些陷阱,每个陷阱占据一块空地。

    一天,国王决定在城堡里布置守卫,他希望安排尽量多的守卫。守卫们都是经过严格训练的,所以一旦他们发现同行或同列中有人的话,他们立即向那人射击。因此,国王希望能够合理地布置守卫,使他们互相之间不能看见,这样他们就不可能互相射击了。守卫们只能被布置在空地上,不能被布置在陷阱或墙上,且一块空地只能布置一个守卫。如果两个守卫在同一行或同一列,并且他们之间没有墙的话,他们就能互相看见。(守卫就像象棋里的车一样)

    你的任务是写一个程序,根据给定的城堡,计算最多可布置多少个守卫,并设计出布置的方案。

【输入】

       第一行两个整数M和N(1≤M,N≤200),表示城堡的规模。

       接下来M行N列的整数,描述的是城堡的地形。第i行j列的数用ai,j表示。

       ai,j=0,表示方格[i,j]是一块空地;

       ai,j=1,表示方格[i,j]是一个陷阱;

       ai,j=2,表示方格[i,j]是墙。

【输出】

       第一行一个整数K,表示最多可布置K个守卫。

       此后K行,每行两个整数xi和yi,描述一个守卫的位置。

【样例】

       guards.in                                    guards.out

       34                                             2

       20 0 0                                       12

       22 2 1                                       33

       01 0 2

       样例数据如图5-2(黑色方格为墙,白色方格为空地,圆圈为陷阱,G表示守卫)

 

 

 

 

 

         

         

         

 

         

         

G

 

 

 

         

         

       ○

 

G

 






这道题标准解法是二分图最大匹配,我用的网络流也过了,实现起来要麻烦一点,速度要慢一点。

标准解法。把所有的横着的连序的块block和竖着的block抽象成点,分为横点和竖点的两个子图,   

两点的连边就确定了一个守卫的位置,然后求最大匹配,这个建图方法比较经典。


我的方法和标准方法比较接近,但是绕了点弯子,就是


这个方法,求一次最大流,最后输出方案的时候弄错了。

我只考虑了与每一个点相连的竖集合到点的残量为零,实际上既要相连的竖集合,还要相连的横集合残量都为零,才能表示守卫的位置。

当时想了很久,但是想起来,跟我们最近做的题相比,这道题确实是很基础

#include <cstdio>

long n;long m;
long map[210][210];
struct node
{
    long x;
    long y;
};
node block[120010];
long size = 0;
const long inf = 0x7fff0000;
long s;long t;
long vd[120010];
long d[120010];

struct node2
{
    long i;
    long f;
    long sol;
    node2* b;
    node2* next;
};
node2* head[120010];

inline long min(long a,long b)
{
    if (a < b) return a;
    return b;
}

long sap(long u,long flow)
{
    if (u == t)
    {
        return flow;
    }
    long count = 0;
    for (node2* vv=head[u];vv;vv=vv->next)
    {
        long v = vv->i;
        if (d[u]-d[v]==1)
        {
            long f = vv->f;
            long tmp = sap(v,min(flow-count,f));
            count += tmp;
            vv->sol += tmp;
            vv->b->sol -= tmp;
            vv->f -= tmp;
            vv->b->f += tmp;
            if (count == flow)
            {
                return count;
            }
        }
    }
    if (d[s]>=size)
    {
        return count;
    }
    vd[d[u]]--;
    if (vd[d[u]]==0)
    {
        d[s] = size;
    }
    d[u]++;
    vd[d[u]]++;
    return count;
}

void insert(long a,long b,long c)
{
    node2* nn = new node2;
    nn->i = b;
    nn->f = c;
    nn->next = head[a];
    nn->sol = 0;
    head[a] = nn;
}

int main()
{
    freopen("guards.in","r",stdin);
    freopen("guards.out","w",stdout);
    scanf("%ld%ld",&n,&m);
    size = 2;s = 1;t = 2;
    for (long i=1;i<n+1;i++)
    {
        for (long j=1;j<m+1;j++)
        {
            scanf("%ld",&map[i][j]);
            if (map[i][j]==0)
            {
                size ++;
                block[size].x = i;
                block[size].y = j;
                map[i][j] = size;
            }
            else if (map[i][j]==1)
                map[i][j] = 0;
            else map[i][j] = -inf;
        }
    }
    for (long i=1;i<n+1;i++)
    {
        for (long j=1;j<m+1;j++)
        {
            if (map[i][j]>0)
            {
                size ++;
                insert(s,size,1);
                insert(size,s,0);
                head[s]->b = head[size];
                head[size]->b = head[s];
                while (map[i][j]>=0&&j<m+1)
                {
                    if (map[i][j]>0)
                    {
                        insert(size,map[i][j],inf);
                        insert(map[i][j],size,0);
                        head[size]->b = head[map[i][j]];
                        head[map[i][j]]->b = head[size];
                    }
                    j++;
                }
                j--;
            }
        }
    }
    for (long j=1;j<m+1;j++)
    {
        for (long i=1;i<n+1;i++)
        {
            if (map[i][j]>0)
            {
                size ++;
                insert(size,t,1);
                insert(t,size,0);
                head[t]->b = head[size];
                head[size]->b = head[t];
                while (map[i][j]>=0&&i<n+1)
                {
                    if (map[i][j]>0)
                    {
                        insert(map[i][j],size,inf);
                        insert(size,map[i][j],0);
                        head[size]->b = head[map[i][j]];
                        head[map[i][j]]->b = head[size];
                    }
                    i++;
                }
                i--;
            }
        }
    }
    vd[0] = size;
    long ans = 0;
    while (d[s]<size)
    {
        ans += sap(s,inf);
    }
    printf("%ld\n",ans);
    for (node2* uu=head[s];uu;uu=uu->next)
    {
        long u = uu->i;
        for (node2* vv=head[u];vv;vv=vv->next)
        {
            long v = vv->i;
            if (vv->sol==1&&head[v]->sol==1)
            {
                printf("%ld %ld\n",block[v].x,block[v].y);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值