码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 2150

时间:2018-10-08 20:46:05      阅读:141      评论:0      收藏:0      [点我收藏+]

标签:pop   stdout   span   next   freopen   str   \n   证明   out   

然后考虑正解

我们发现,最坏情况就是每个点都派驻军队,所以答案至多是“.”的数目

而且,每个点都至多只有一个入度和一个出度,所以我们可以将每个点拆成两个点,一个作为入点,一个作为出点,然后所有图上能到达的点由出点向入点建图

这样整个图就形成了一个二分图

然后在整个图上跑二分图匹配即可

答案即为“.”点数-二分图最大匹配数

稍微证明一下:我们的目的是最大化有入边的点的数量,那我们仅需让入点能更多的被匹配上即可

那也就是二分图匹配喽

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct Edge
{
    int next;
    int to;
}edge[10005];
int head[2505];
int maps[55][55];
char s[55];
bool used[6005];
int f[6005];
int n,m,r,c;
int cnt=1,cot;
queue <int> Q;
bool check(int x,int y)
{
    if(x>0&&x<=n&&y>0&&y<=m&&maps[x][y])
    {
        return 1;
    }
    return 0;
}
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void add(int l,int r)
{
    edge[cnt].next=head[l];
    edge[cnt].to=r;
    head[l]=cnt++;
}
int po(int x,int y)
{
    return (x-1)*m+y; 
}
int ppo(int x,int y)
{
    return (x-1)*m+y+n*m;
}
bool dfs(int x)
{
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int to=edge[i].to;
        if(used[to])
        {
            continue;
        }
        used[to]=1;
        if(!f[to]||dfs(f[to]))
        {
            f[to]=x;
            return 1;
        }
    }
    return 0;
}
int hungary()
{
    int ret=0;
    while(!Q.empty())
    {
        memset(used,0,sizeof(used));
        int u=Q.front();
        Q.pop();
        if(dfs(u))
        {
            ret++;
        }
    }
    return ret;
}
int main()
{
    freopen("legion.in","r",stdin);
    freopen("legion.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&r,&c);
    init();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;j++)
        {
            if(s[j]==.)
            {
                maps[i][j]=1;
                Q.push(po(i,j));
                cot++;
            }else
            {
                maps[i][j]=0;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int x0=i+r;
            int y0=j+c;
            if(check(x0,y0))
            {
                add(po(i,j),ppo(x0,y0));
            }
            x0=i+r;
            y0=j-c;
            if(check(x0,y0))
            {
                add(po(i,j),ppo(x0,y0));
            }
            x0=i+c;
            y0=j+r;
            if(check(x0,y0))
            {
                add(po(i,j),ppo(x0,y0));
            }
            x0=i+c;
            y0=j-r;
            if(check(x0,y0))
            {
                add(po(i,j),ppo(x0,y0));
            }
        }
    }
    printf("%d\n",cot-hungary());
    return 0;
}

 

bzoj 2150

标签:pop   stdout   span   next   freopen   str   \n   证明   out   

原文地址:https://www.cnblogs.com/zhangleo/p/9756492.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!