【HDU5556 2015合肥赛区E】【最大团or二分图匹配】Land of Farms 不同编号不相邻条件下的最大农场数


Land of Farms

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 68    Accepted Submission(s): 34


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×M grids. 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 ( T200 ).

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 ( 1N,M10 ). 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
 


【HDU5556 2015合肥赛区E】【最大团or二分图匹配】Land of Farms 不同编号不相邻条件下的最大农场数 最大团做法

#include<stdio.h> 
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=12,M=0,Z=1e9+7,ms63=1061109567;
const int dy[4]={-1,0,0,1};
const int dx[4]={0,-1,1,0};
int casenum,casei;
int n,m,id;
char a[N][N];		//原始地图
int o[N][N];		//节点标号
bool e[105][105];	//表示最大团关系,e[i][j]==1表示i与j之间没有排斥关系
int f[105][105];	//把存边方式从邻接矩阵边转化为邻接链表,以此来加快速度
int mx[105];		//mx[i]表示最大团中编号最小的点的编号>=i时的最大团答案
int ans;			//全局变量,最后得到的ans就是全局最大团
void color(int y,int x,char ch)
{
    if(y<0||y==n||x<0||x==m||a[y][x]!=ch)return;
    if(~o[y][x])return;o[y][x]=id;
    for(int i=0;i<4;++i)color(y+dy[i],x+dx[i],ch);
}
void disable(int y1,int x1,int y2,int x2)
{
    if(y2<0||y2==n||x2<0||x2==m)return;
    int o1=o[y1][x1];
    int o2=o[y2][x2];
    if(o1==o2)return;
    e[o1][o2]=e[o2][o1]=0;
}
void COLOR()
{
    id=0;MS(o,-1);
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<m;++j)
        {
            if(a[i][j]=='.')
			{
				o[i][j]=id++;
			}
            else if(o[i][j]==-1)
            {
                color(i,j,a[i][j]);
                id++;
            }
        }
    }
}
void build_graph()
{
    MS(e,1);
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<m;++j)
        {
            for(int k=0;k<4;k++)
            {
                disable(i,j,i+dy[k],j+dx[k]);
            }
        }
    }
}
int mcp(int num,int p)
{
    if(!p)//没有边则直接更新答案
    {
        if(num>ans){ans=num;return 1;}
        else return 0;
    }
    for(int i=0;i<p;++i)//枚举当前节点可以延展的所有节点
    {
		//如果把接下来的所有点都加入最大团中,还无法更新答案的话,返回no
        if(num+p-i<=ans)return 0;
		//如果把目前编号最小的节点所能延展的所有点都加进最大团中,还无法更新答案的话,返回no
        int u=f[num][i];if(mx[u]+num<=ans)return 0;
        int k=0;
        for(int j=i+1;j<p;++j)if(e[u][f[num][j]])f[num+1][k++]=f[num][j];
        if(mcp(num+1,k))return 1;
    }
    return 0;
}
void MCP()
{
    MS(f,0);
	MS(mx,0);
	ans=0;
	//从后向前,枚举每个点i为最大团中编号最小的点,以此为起点展开搜索
    for(int i=id-1;~i;--i)
    {
		int k=0;
		for(int j=i+1;j<id;++j)if(e[i][j])f[1][k++]=j;
		mcp(1,k);
		mx[i]=ans;
    }
}
int main()
{
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;++casei)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i)scanf("%s",a[i]);
		COLOR();
		build_graph();
		MCP();
        printf("Case #%d: %d\n",casei,ans);
    }
}
/*
【trick&&吐槽】
题意说不清,题意有毒……这时需要枚举题意,猜解样例Orz

【题意】
给你一个n(10)*m(10)的棋盘。
格子上的'.'代表可以建造新式农场的点,每个新建的农场是独立的,拥有独立的编号。
格子上的'1'~'9'代表可以建造上古农场的点,相同的数字构成的联通块代表相同的上古农场,相同的上古农场共享一个编号。
如果我们要建造一个上古农场,就必须建立其整个联通块。
对于一个编号的农场,其上下左右相邻处不能有其他编号的农场。
基于以上原则,我们希望建造尽可能多数量的农场。
让你输出这个最大数量。

【类型】
最大团or二分图匹配

【分析】
首先看到这道题点数很少,最大供选择农场数才不过100,而且涉及到"我选了你就不能选"这样的限制条件,于是我们考虑到最大团。
于是——
第一步:求出所有联通块,也就是可能的不同编号的农场数量,并进行编号、染色。
第二步:然后初始化一个全为1的bool矩阵,e[i][j]==1的话表示i和j编号的农场可以同时选。
之后我们对于每个点,向上下左右四个方向走,如果编号不同,那么不同这2个农场不能同时选,在e[][]中标记。
之后对e[][]跑个最大团即可。
本代码里的最大团模板是0base,就是节点编号从0开始,速度很快哦~~啦啦啦!

然而,这道题的正解是二分图匹配,其时间复杂度为O(10^2 * nm * VE)。
O(VE)是匈牙利算法的时间复杂度,最坏情况下是100*200,也就是可以控制使得单组时间复杂度为2e7。

【数据】
1
10 10
..........
..........
..........
..........
..........
..........
..........
..........
..........
..........

*/

【HDU5556 2015合肥赛区E】【最大团or二分图匹配】Land of Farms 不同编号不相邻条件下的最大农场数 匈牙利做法

#include<stdio.h> 
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=12,M=0,Z=1e9+7,ms63=1061109567;
const int dy[4]={-1,0,0,1};
const int dx[4]={0,-1,1,0};
int casenum,casei;
int n,m,num,ans;
char a[N][N];
bool e[N][N];		//e[i][j]==1表示(i,j)这个'.'是可以通过二分匹配的方式选取的
bool u[10];			//u[i]==1表示'0'+i这个数字是被我们枚举选取的
vector<int>b[105];	//b[i]存放i号点(i号点为偶点)可以匹配的点的编号
int p[105];			//p[j]表示j号点(j号点为奇点)实现匹配的点的编号
bool vis[105];		//vis[i]表示i号点(i号点是偶点)是否匹配访问过
map<int,int>mop;
bool color()
{
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)if(isdigit(a[i][j])&&u[a[i][j]-'0'])
		{
			for(int d=0;d<4;d++)
			{
				int y=i+dy[d];
				int x=j+dx[d];
				if(y<0||y==n||x<0||x==m)continue;
				if(a[y][x]=='.')e[y][x]=0;
				else if(a[y][x]!=a[i][j]&&u[a[y][x]-'0'])return 0;
			}
		}
	}
	return 1;
}
void build_graph()
{
	for(int i=0;i<n*m;i++)b[i].clear();
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)if(a[i][j]=='.'&&e[i][j]&&(i+j)%2==0)
		{

			for(int d=0;d<4;d++)
			{
				int y=i+dy[d];
				int x=j+dx[d];
				if(y<0||y==n||x<0||x==m||a[y][x]!='.'||!e[y][x]||(y+x)%2==0)continue;
				int o1=i*m+j;
				int o2=y*m+x;
				b[o1].push_back(o2);
			}
		}
	}
}
int hungary(int x)
{
	vis[x]=1;
	for(int i=b[x].size()-1;~i;i--)
	{
		int y=b[x][i];
		if(p[y]==-1){p[y]=x;return 1;}
	}
	for(int i=b[x].size()-1;~i;i--)
	{
		int y=b[x][i];
		if(!vis[p[y]]&&hungary(p[y])){p[y]=x;return 1;}
	}
	return 0;
}
void HUNGARY()
{
	MS(p,-1);
	int sum=0;
	int tmp=0;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)if(a[i][j]=='.'&&e[i][j])
		{
			++sum;
			MS(vis,0);
			if(hungary(i*m+j))++tmp;
		}
	}
	gmax(ans,sum-tmp+num);
}
int main()
{
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;++casei)
    {
        scanf("%d%d",&n,&m);
		ans=0;
        for(int i=0;i<n;++i)scanf("%s",a[i]);
		mop.clear();int id=0;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)if(isdigit(a[i][j]))
			{
				if(mop.find(a[i][j])==mop.end())mop[a[i][j]]=id++;
				a[i][j]=mop[a[i][j]]+'0';
			}
		}
		int top=1<<id;
		for(int i=0;i<top;i++)
		{
			MS(e,1);
			MS(u,0);
			int p=0;
			int x=i;
			num=0;
			while(x)
			{
				if(x&1)u[p]=1,++num;
				x>>=1;
				++p;
			}
			if(color())
			{
				build_graph();
				HUNGARY();
			}
		}
        printf("Case #%d: %d\n",casei,ans);
    }
}
/*
【trick&&吐槽】
题意说不清,题意有毒……这时需要枚举题意,猜解样例Orz

【题意】
给你一个n(10)*m(10)的棋盘。
格子上的'.'代表可以建造新式农场的点,每个新建的农场是独立的,拥有独立的编号。
格子上的'1'~'9'代表可以建造上古农场的点,相同的数字构成的联通块代表相同的上古农场,相同的上古农场共享一个编号。
如果我们要建造一个上古农场,就必须建立其整个联通块。
对于一个编号的农场,其上下左右相邻处不能有其他编号的农场。
基于以上原则,我们希望建造尽可能多数量的农场。
让你输出这个最大数量。

【类型】
最大团or二分图匹配

【分析】
2^10枚举选取的远古农场,
然后把选取的远古农场周围的'.'标记为不可选取。
对于剩余的可以选取的'.',从奇点向相邻的偶点连边,
然后跑二分图最大匹配,
用"可以选取的'.'总数-最大匹配数+枚举选取的远古农场数"更新答案

【数据】
input
1
10 10
..........
..........
..........
..........
..........
..........
..........
..........
..........
..........
output
50

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值