『HDU 5556』Land of Farms

题目链接: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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值