题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5556
转载链接:http://blog.csdn.net/mengxiang000000/article/details/52712864
题意:(题意特别难理解)有个农夫,想在发现的新大陆中开荒养羊, '.'代表没开荒的地, ‘数字’代表已经开荒过的旧农场. 农夫呢想做新农场尽可能多,而且新农场和新农场之间不能公用边界
(就是两个点不能靠在一起,必须隔开,例如‘1’‘2’是错误的,必须‘1’‘*’‘2’ 用格子隔开)… 那么旧农场也可以边新农场,只不过,凡是属于你选择的这个旧农场的所有地方都必须选择,那么最多可以开荒多少个农场
个人感想:最近在刷一些区域赛的题目,一拿到这题我便无从下手.我都不知道该怎么做这题,这道题属于我的知识范围体系外的,我并不知道有最小点覆盖这种东西,完全没感觉… 感觉区域赛的题目真的什么都得考,什么都得会,真是难啊…
好吧说说我的理解:
首先看旧农场的数据范围,很明显 最多就(0-9)个,那么我们可以通过暴力来决定这些旧农场选择还是不选择,决定好之后,我们就枚举哪一些“.”我们是可以选择的,某些点如果在我们选择的旧农场旁边就是无效的. 决定之后再将可以选的点全部连边,最一次最小点覆盖即可.
注意原理:最大匹配数==最小点覆盖
(可是这里是无向边,有双边,所以得/2)
分析:二分图最小点覆盖
/* Author:GavinjouElephant
* Title:
* Number:
* main meanning:
*
*
*
*/
#include <iostream>
using namespace std;
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <cctype>
#include <vector>
#include <set>
#include <cstdlib>
#include <map>
#include <queue>
//#include<initializer_list>
//#include <windows.h>
//#include <fstream>
//#include <conio.h>
#define MaxN 0x7fffffff
#define MinN -0x7fffffff
#define lson 2*k
#define rson 2*k+1
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
int Scan()//读入整数外挂.
{
int res = 0, ch, flag = 0;
if((ch = getchar()) == '-') //判断正负
flag = 1;
else if(ch >= '0' && ch <= '9') //得到完整的数
res = ch - '0';
while((ch = getchar()) >= '0' && ch <= '9' )
res = res * 10 + ch - '0';
return flag ? -res : res;
}
void Out(int a) //输出外挂
{
if(a>9)
Out(a/10);
putchar(a%10+'0');
}
int T;
int n,m;
char g[15][15];
int now[15][15];
int v[15][15];
int num[15][15];
int ary[10];
int Se;
int vis[10];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
vector<int>G[105];
int match[105];
int vis2[105];
int top;
int ans;
int ff(int u)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(vis2[v]==0)
{
vis2[v]=1;
if(match[v]==-1||ff(match[v]))
{
match[v]=u;
return 1;
}
}
}
return 0;
}
void Tarjan()
{
int output=0;
memset(match,-1,sizeof(match));
for(int i=1;i<=top;i++)
{
memset(vis2,0,sizeof(vis2));
if(ff(i)==1) output++;
}
int sum=0;
for(int i=0;i<Se;i++) if(vis[i]) sum++;
ans=max(ans,sum+top-output/2);
}
void solve()
{
memset(v,0,sizeof(v));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(now[i][j]==-1)
{
for(int k=0;k<4;k++)
{
int x=i+dx[k];
int y=j+dy[k];
if(x>=0&&x<n&&y>=0&&y<m)
{
if(now[x][y]!=-1&&vis[now[x][y]])
{
v[i][j]=1;
}
}
}
}
else
{
v[i][j]=1;//档前位置必不能选
for(int k=0;k<4;k++)
{
int x=i+dx[k];
int y=j+dy[k];
if(x>=0&&x<n&&y>=0&&y<m)
{
if(now[x][y]!=-1&&vis[now[x][y]]&&vis[now[i][j]]&&now[x][y]!=now[i][j])//如果这两个城市必须选择,而且在临近的,这种选择方法不成立
{
return ;
}
}
}
}
}
}
for(int i=0;i<n*m+1;i++)G[i].clear();
memset(num,0,sizeof(num));
top=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(v[i][j]==0)
{
num[i][j]=++top;
}
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(num[i][j]!=0)
{
for(int k=0;k<4;k++)
{
int x=i+dx[k];
int y=j+dy[k];
if(x>=0&&x<n&&y>=0&&y<m)
{
if(num[x][y]!=0)
{
G[num[i][j]].push_back(num[x][y]);
}
}
}
}
}
}
Tarjan();
}
void Dfs(int s)
{
if(s==Se)
{
solve();
return ;
}
vis[s]=1;
Dfs(s+1);
vis[s]=0;
Dfs(s+1);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("coco.txt","r",stdin);
freopen("lala.txt","w",stdout);
#endif
scanf("%d",&T);
for(int cas=1;cas<=T;cas++)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%s",g[i]);
memset(ary,-1,sizeof(ary));
memset(now,-1,sizeof(now));
Se=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(g[i][j]!='.')
{
if(ary[g[i][j]-'0']==-1)
{
ary[g[i][j]-'0']=(Se++);
now[i][j]=ary[g[i][j]-'0'];
}
else now[i][j]=ary[g[i][j]-'0'];
}
}
}
// for(int i=0;i<n;i++)
// {
// for(int j=0;j<m;j++)
// {
// cout<<now[i][j]<<" ";
// }
// cout<<endl;
// }
ans=0;
memset(vis,0,sizeof(vis));
Dfs(0);
printf("Case #%d: %d\n",cas,ans);
}
return 0;
}