bzoj 2150 部落戰爭 (二分圖匹配)

題目大意:給你一個n*m的棋盤,有一些壞點不能走,你有很多軍隊,每支軍隊可以像象棋裏的馬一樣移動,不過馬是1*2移動的,而軍隊是r*c移動的,軍隊只能從上往下移動,如果一個點已經被一直軍隊經過,那麼其他軍隊不能再經過這個點,求覆蓋所有非壞點的最少軍隊數

洛谷P2172傳送門

對在某個點的軍隊可能跳的位置建邊。把圖摳出來,因爲軍隊只能從上往下移動,所以他移動的方式就是是一條鏈,所以答案就是把這個圖分成若干條鏈,求鏈的數量的最小值

這不就是二分圖匹配麼

對於一個鏈,除了端點,每個點都需要一個出邊一個入邊,跑匈牙利就行了

注意,每個點跑匈牙利之前都要清一次vis數組!才能正確表示某個點在這次匹配中是否已經被使用

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 60
#define mod 20100403
#define p(i,j) ((i-1)*m+j)
#define dd double
using namespace std;

char str[N][N];
int n,m,r,c,cte;
int mp[N][N],head[N*N],vis[N*N],mch[N*N];
int xx[4],yy[4];
struct Edge{int to,nxt;}edge[N*N*10];
void ae(int u,int v){
    ++cte,edge[cte].to=v;
    edge[cte].nxt=head[u];
    head[u]=cte;
}
int check(int x,int y){
    if(x<1||y<1||x>n||y>m||mp[x][y]) return 0;
    return 1;
}
void build_edge()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(mp[i][j]) continue;
            for(int k=0;k<4;k++)
                if(check(i+xx[k],j+yy[k]))
                    ae(p(i,j),p(i+xx[k],j+yy[k]));
        }
}
int Hungary(int x)
{
    for(int j=head[x];j!=-1;j=edge[j].nxt){        
        int v=edge[j].to;
        if(!vis[v])
        {
            vis[v]=1;
            if(!mch[v]||Hungary(mch[v])){
                mch[v]=x;
                return 1;
            }
        }
    }return 0;
}


int main()
{
    scanf("%d%d%d%d",&n,&m,&r,&c);
    for(int i=1;i<=n;i++){
        scanf("%s",str[i]+1);
        for(int j=1;j<=m;j++)
            if(str[i][j]=='.') mp[i][j]=0;
            else mp[i][j]=1;
    }
    xx[0]=r,xx[1]=r,xx[2]=c,xx[3]=c;
    yy[0]=c,yy[1]=-c,yy[2]=r,yy[3]=-r;
    int ans=0;
    memset(head,-1,sizeof(head));
    build_edge();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(mp[i][j]) continue;
            memset(vis,0,sizeof(vis));
            if(!Hungary(p(i,j))) ans++;}
    printf("%d\n",ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章