hdu 5556 Land of Farms【思维+暴力枚举+最大独立集】好题!

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的矩阵,其中“.”代表空地,“0-9”代表古代建筑,我们如果选择了一个编号的古代建筑想要建立,那么对应就要将全部该编号的建筑建立起来,如果在空地上建筑,只建立当前点。问最多能够建立多少种建筑,并且每两种建筑之间没有公共边。

题目意识比较难懂,我们来分析一下第一组样例:

..3.
023.
.211
对应可以建出来这样的效果:
.X.Y
0...
..11
那么对应有4种建筑,输出4.


思路:


1、首先如果我们没有这些古代建筑,只是一个N*M的一个矩阵的话,我们按照点的坐标的奇偶性((x+y)%2)将矩阵分成一个二分图,然后将每个点与其周围四个点(有公共边)建立一条无向边,表示这两个点如果在一起建立是矛盾的。

那么不难理解:我们现在跑一遍匈牙利算法求得的最大二分匹配数==最小点覆盖数*2(因为是无向边).那么如果我们将属于最小点覆盖集的点去掉,剩下的点就是属于最大独立集的点,那么此时能够建立的最多的建筑的数目:n*m-最大匹配数/2【最大独立集=n-最大二分匹配数】


2、那么现在引入这些古代建筑,题干中保证了这些古代建筑一共只有10种(0-9),那么我们可以暴力枚举一下哪些古代建筑我们建立起来,哪些古代建筑我们不建立。对应建立起来的古代建组周围的“.”的点我们抛弃掉,建立起来的古代建筑的点也抛弃掉,没有建立起来的古代建筑的点也抛弃掉。剩下的“.”,我们按照第一步的思路建二分图,然后求最大独立集即可。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
vector<int >mp[50*50];
int num[50][50];
int match[50*50];
int vis2[50*50];
int map[15][15];
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
char a[15][15];
int use[10];
int vis[10];
int n,m,contz,ans,contn;
int find(int u)
{
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(vis2[v]==0)
        {
            vis2[v]=1;
            if(match[v]==-1||find(match[v]))
            {
                match[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
void Slove()
{
    int output=0;
    memset(match,-1,sizeof(match));
    for(int i=1;i<=contn;i++)
    {
        memset(vis2,0,sizeof(vis2));
        if(find(i)==1)output++;
    }
    int sum=0;
    for(int i=0;i<contz;i++)if(vis[i]==1)sum++;
    ans=max(ans,sum+contn-output/2);
}
void Getmap()
{
    memset(map,0,sizeof(map));
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(a[i][j]=='.')
            {
                for(int k=0;k<4;k++)
                {
                    int x=i+fx[k];
                    int y=j+fy[k];
                    if(x>=0&&x<n&&y>=0&&y<m&&a[x][y]!='.')
                    {
                        if(vis[a[x][y]-'0']==1)
                        {
                            map[i][j]=1;
                        }
                    }
                }
                continue;
            }
            else
            {
                map[i][j]=1;
                for(int k=0;k<4;k++)
                {
                    int x=i+fx[k];
                    int y=j+fy[k];
                    if(x>=0&&x<n&&y>=0&&y<m)
                    {
                        if(vis[a[i][j]-'0']==1&&vis[a[x][y]-'0']==1&&a[i][j]!=a[x][y])
                        {
                            return ;
                        }
                    }
                }
            }
        }
    }
    for(int i=1;i<=n*m;i++)mp[i].clear();
    contn=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(map[i][j]==0)
            {
                num[i][j]=++contn;
            }
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(map[i][j]==0)
            {
                for(int k=0;k<4;k++)
                {
                    int x=i+fx[k];
                    int y=j+fy[k];
                    if(x>=0&&x<n&&y>=0&&y<m&&map[x][y]==0)
                    {
                        mp[num[i][j]].push_back(num[x][y]);
                    }
                }
            }
        }
    }
    Slove();
}
void Dfs(int now)
{
    if(now==contz)
    {
        Getmap();
        return ;
    }
    vis[now]=1;
    Dfs(now+1);
    vis[now]=0;
    Dfs(now+1);
}
int main()
{
    int t;
    int kase=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%s",a[i]);
        }
        memset(use,-1,sizeof(use));
        contz=0;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(a[i][j]=='.')continue;
                if(use[a[i][j]-'0']==-1)
                {
                    use[a[i][j]-'0']=contz++;
                    a[i][j]=use[a[i][j]-'0']+'0';
                }
                else
                {
                    a[i][j]=use[a[i][j]-'0']+'0';
                }
            }
        }
        ans=0;
        Dfs(0);
        printf("Case #%d: %d\n",++kase,ans);
    }
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值