HDU 5556 Land of Farms(枚举+二分图匹配)

87 篇文章 0 订阅

Land of Farms

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 526 Accepted Submission(s): 174
Problem Description
Farmer John and his brothers have found a new land. They are so excited and decide to build new farms on the land. The land is a rectangle and consists of N×Mgrids. A farm consists of one or more connected grids. Two grids are adjacent if they share a common border, i.e. their Manhattan distance is exactly 1. In a farm, two grids are considered connected if there exist a series of adjacent grids, which also belong to that farm, between them.

Farmer John wants to build as many farms as possible on the new land. It is required that any two farms should not be adjacent. Otherwise, sheep from different farms would fight on the border. This should be an easy task until several ancient farms are discovered.

Each of the ancient farms also consists of one or more connected grids. Due to the respect to the ancient farmers, Farmer John do not want to divide any ancient farm. If a grid from an ancient farm is selected in a new farm, other grids from the ancient farm should also be selected in the new farm. Note that the ancient farms may be adjacent, because ancient sheep do not fight each other.

The problem is a little complicated now. Can you help Farmer John to find a plan with the maximum number of farms?
Input
The first line of input contains a number T indicating the number of test cases (T≤200).

Each test case starts with a line containing two integers N and M, indicating the size of the land. Each of the following N lines contains M characters, describing the map of the land (1≤N,M≤10). A grid of an ancient farm is indicated by a single digit (0-9). Grids with the same digit belong to the same ancient farm. Other grids are denoted with a single character “.”. It is guaranteed that all test cases are valid.
Output
For each test case, output a single line consisting of “Case #X: Y”. X is the test case number starting from 1. Y is the maximum number of new farms.
Sample Input
3
3 4
..3.
023.
.211
2 3


4 4
1111
1..1
1991
1111
Sample Output
Case #1: 4
Case #2: 3
Case #3: 1
Source
2015ACM/ICPC亚洲区合肥站-重现赛(感谢中科大)

题目大意

  在一个 N×M(N,M10) 的矩阵中有最多 10 个古代牧场,每个古代牧场可能占多个格子,其它地方为现代牧场,每个牧场只占一个格子。问要使选择的牧场互不相邻(有公共边视为相邻),最多能选择几个。

解题思路

  首先很容易想到,如果把每个牧场看作一个顶点,那么矩阵就形成了一个二分图,但是由于古代牧场的存在,这张图退化成了一张一般图,题目所求即为最大独立集,而且全图最多只有 100 个结点,所以可以转化成补图的最大团问题,使用Bron–Kerbosch算法求解。然而这样写的话最终会TLE,所以我们需要优化。考虑到整张图如果不考虑古代牧场就是二分图,而且古代牧场最多只有 10 个,所以我们可以枚举使用了哪些古代牧场,然后对与其不相邻的现代牧场二分图匹配求最大独立集。

AC代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <bitset>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define ULL unsigned long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))
#define sqr(x) ((x)*(x))

const int MAXN=10+3;
const int MAXV=MAXN*MAXN;
const int dy[]={-1, 0, 1, 0}, dx[]={0, 1, 0, -1};

int N, M, num;
char s[MAXN][MAXN];
int maze[MAXN][MAXN];
vector<int> save;
int ans;
int num_x, num_y;
vector<int> G[MAXV];//图的邻接表形式(左边顶点在前面,只需要建立从左边指向右边的边即可)
int match_x[MAXV],match_y[MAXV];//顶点匹配对象
int dis,dis_x[MAXV],dis_y[MAXV];//距离(用于多路增广)
bool used[MAXV];

bool searchP()//标记距离(用于多路增广)
{
    queue<int> que;
    dis=INF;
    mem(dis_x,-1);
    mem(dis_y,-1);
    for(int i=0;i<num_x;++i)
        if(match_x[i]==-1)
        {
            que.push(i);
            dis_x[i]=0;
        }
    while(!que.empty())
    {
        int u=que.front(); que.pop();
        if(dis_x[u]>dis)
            break;
        for(int i=0;i<G[u].size();++i)
        {
            int v=G[u][i];
            if(dis_y[v]==-1)
            {
                dis_y[v]=dis_x[u]+1;
                if(match_y[v]==-1)
                    dis=dis_y[v];
                else
                {
                    dis_x[match_y[v]]=dis_y[v]+1;
                    que.push(match_y[v]);
                }
            }
        }
    }
    return dis!=INF;
}

bool dfs(int u)//dfs增广
{
    for(int i=0;i<G[u].size();++i)
    {
        int v=G[u][i];
        if(!used[v]&&dis_y[v]==dis_x[u]+1)
        {
            used[v]=true;
            if(match_y[v]!=-1&&dis_y[v]==dis)
                continue;
            if(match_y[v]==-1||dfs(match_y[v]))
            {
                match_y[v]=u;
                match_x[u]=v;
                return true;
            }
        }
    }
    return false;
}

int hopcroft_carp()
{
    int res=0;
    mem(match_x,-1);
    mem(match_y,-1);
    while(searchP())
    {
        mem(used,0);
        for(int i=0;i<num_x;++i)
            if(match_x[i]==-1&&dfs(i))
                ++res;
    }
    return res;
}

void init()
{
    ans=0;
    save.clear();
}

inline bool in(const int &y, const int &x)
{
    return y>=0 && y<N && x>=0 && x<M;
}

bool judge(int y, int x, int s)//检查是否与选定的古代牧场相邻
{
    for(int i=0;i<4;++i)
    {
        int ny=y+dy[i], nx=x+dx[i];
        if(in(ny, nx) && maze[ny][nx]!=-1 && (s>>maze[ny][nx])&1)
            return false;
    }
    return true;
}

void solve(int s)
{
    num_x=num_y=0;
    for(int i=0;i<N*M;++i)
        G[i].clear();
    for(int y=0;y<N;++y)
        for(int x=0;x<M;++x)
            if(maze[y][x]==-1)
            {
                if(!judge(y, x, s))
                    continue;
                if(!((y+x)&1))
                {
                    for(int i=0;i<4;++i)
                    {
                        int ny=y+dy[i], nx=x+dx[i];
                        if(in(ny, nx) && maze[ny][nx]==-1 && judge(ny, nx, s))
                            G[num_x].push_back(ny*M+nx);
                    }
                    ++num_x;
                }
                else ++num_y;
            }
            else if((s>>maze[y][x])&1)
            {
                for(int i=0;i<4;++i)
                {
                    int ny=y+dy[i], nx=x+dx[i];
                    if(in(ny, nx) && maze[ny][nx]!=-1 && maze[ny][nx]!=maze[y][x] && ((s>>maze[ny][nx])&1))
                        return ;
                }
            }
    ans=max(ans,  num_x+num_y-hopcroft_carp()+__builtin_popcount(s));
}

int main()
{
    int T_T;
    scanf("%d", &T_T);
    for(int cas=1;cas<=T_T;++cas)
    {
        scanf("%d%d", &N, &M);
        init();
        for(int i=0;i<N;++i)
        {
            scanf("%s", s[i]);
            for(int j=0;j<M;++j)
                if(s[i][j]>='0' && s[i][j]<='9')
                    save.push_back(s[i][j]-'0');
        }
        sort(save.begin(), save.end());
        save.erase(unique(save.begin(), save.end()), save.end());
        num=save.size();
        for(int i=0;i<N;++i)
            for(int j=0;j<M;++j)
                if(s[i][j]>='0' && s[i][j]<='9')
                    maze[i][j]=lower_bound(save.begin(), save.end(), s[i][j]-'0')-save.begin();
                else maze[i][j]=-1;
        int S=(1<<num);
        for(int s=0;s<S;++s)//枚举选择的古代牧场
            solve(s);
        printf("Case #%d: %d\n", cas, ans);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值