BZOJ2150: 部落战争

【传送门:BZOJ2150


简要题意:

  给出一个矩阵,矩阵上的字符有两种,一种是'x',表示山洞(不可走),一种是'.',表示城镇

  可以在城镇处放士兵,士兵经过的每个城镇都会被占领,士兵只能向下走,而且行走的方式和马相似,不过马走的是1*2,士兵走的是R*C,士兵不能经过一个被占领的城镇

  求出最少派出多少个士兵能够占领所有城镇


题解:

  最小路径覆盖问题,用二分图匹配

  将所有城镇拆成两个集合

  如果x能走到y,则x连向y的第二个集合的点

  然后将总点数-最大匹配数就是最小路径覆盖数了


参考代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
char st[51][51];
struct node
{
    int x,y,next;
}a[21000];int len,last[6100];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int dx[5],dy[5];
int n,m,r,c;
int p(int x,int y){return (x-1)*m+y;}
int match[6100],chw[6100];
bool findmuniu(int x,int t)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(chw[y]!=t)
        {
            chw[y]=t;
            if(match[y]==0||findmuniu(match[y],t)==true)
            {
                match[y]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&r,&c);
    for(int i=1;i<=n;i++) scanf("%s",st[i]+1);
    len=0;memset(last,0,sizeof(last));
    dx[1]=r;dy[1]=-c;dx[2]=r;dy[2]=c;
    dx[3]=c;dy[3]=-r;dx[4]=c;dy[4]=r;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(st[i][j]=='.')
            {
                sum++;
                for(int k=1;k<=4;k++)
                {
                    int tx=i+dx[k],ty=j+dy[k];
                    if(tx<1||ty<1||tx>n||ty>m||st[tx][ty]=='x') continue;
                    ins(p(i,j),p(tx,ty)+n*m);
                }
            }
        }
    }
    memset(chw,0,sizeof(chw));
    memset(match,0,sizeof(match));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(st[i][j]=='.')
            {
                int t=p(i,j);
                if(findmuniu(t,t)==true) sum--;
            }
        }
    }
    printf("%d
",sum);
    return 0;
}