zoj1654 Place the Robots

题目描述: 

Robert 是一个著名的工程师。一天,他的老板给他分配了一个任务。任务的背景是:给定一 
个m×n 大小的地图,地图由方格组成,在地图中有3 种方格-墙、草地和空地,他的老板希望 
能在地图中放置尽可能多的机器人。每个机器人都配备了激光枪,可以同时向四个方向(上、下、 
左、右)开枪。机器人一直待在最初始放置的方格处,不可移动,然后一直朝四个方向开枪。激 
光枪发射出的激光可以穿透草地,但不能穿透墙壁。机器人只能放置在空地。当然,老板不希望 
机器人互相攻击,也就是说,两个机器人不能放在同一行(水平或垂直),除非他们之间有一堵墙 
格开。 
给定一张地图,你的程序需要输出在该地图中可以放置的机器人的最大数目。

思路: 
以样例解释

这里写图片描述
在问题的原型中,草地,墙这些信息不是本题所关心的,本题关心的只是空地和空地之间的联系。因此,很自然想到了下面这种简单的模型:以空地为顶点,在有冲突的空地间连边。 
这里写图片描述这里写图片描述

把所有的空地用数字标明,得到a图: 
这里写图片描述

把所有有冲突的空地间用边连接后得到图(b): 
这里写图片描述

于是,问题转化为求图的最大独立集问题:求最大顶点集合,集合中所有顶点互不连接(即互不冲突)。但是最大点独立集问题是一个NP 问题,没有有效的算法能求解。

———————————————————————————————————————————————————————————————————————— 
将每一行被墙隔开、且包含空地的连续区域称作“块”。显然,在一个块之中,最多只能放一个机器人。把这些块编上号,如图7.25(a)所示。需要说明的是,最后一行,即第4 行有两个空地,但这两个空地之间没有墙壁,只有草地,所以这两个空地应该属于同一“块”。同样,把竖直方向的块也编上号,如图7.25(b)所示。

这里写图片描述
把每个横向块看作二部图中顶点集合X 中的顶点,竖向块看作集合Y 中的顶点,若两个块有公共的空地(注意,每两个块最多有一个公共空地),则在它们之间连边。例如,横向块2 和竖向块1 有公共的空地,即(2, 0),于是在X 集合中的顶点2 和Y 集合中的顶点1 之间有一条边。这样,问题转化成一个二部图,如图7.25(c)所示。由于每条边表示一个空地(即一个横向块和一个竖向块的公共空地),有冲突的空地之间必有公共顶点。例如边(x1, y1)表示空地(0, 0)、边(x2, y1)表示空地(2, 0),在这两个空地上不能同时放置机器人。所以问题转化为在二部图中找没有公共顶点的最大边集,这就是最大匹配问题。

以上面给的样例的构造结果: 
这里写图片描述

匈牙利算法 O( VE ) 


//题目详解描述转自http://m.blog.csdn.net/blog/u014141559/44409255

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
//#include<map>
#include<stack>
#pragma comment(linker,"/STACK:102400000,102400000")
#define pi acos(-1.0)
#define EPS 1e-6
#define INF (1<<24)
using namespace std;
int m,n;


char map[55][55];  //地图
int from[2555];   //from[y]表示与Yi匹配的X顶点
bool use[2555];
int xs[55][55],ys[55][55];  //水平方向上块的编号,垂直方向上块的编号
vector<int>g[2555];   //g[i]表示与左边点i相连的右边的点
int nx,ny;   //水平方向上块的个数,垂直方向上块的个数


bool match(int x)
{
    for(int i=0;i<g[x].size();i++)
    {
        if(!use[g[x][i]])
        {
            use[g[x][i]]=true;
            if(from[g[x][i]]==-1||match(from[g[x][i]]))
            {
                from[g[x][i]]=x;
                return true;
            }
        }
    }
    return false;
}
int hungary()
{
    int tot=0;
    memset(from,255,sizeof(from));
    for(int i=1;i<=nx;i++)
    {
        memset(use,0,sizeof(use));
        if(match(i)) tot++;
    }
    return tot;  //最大匹配数
}
/*二分图最大匹配*/
/*匈牙利算法*/


int main()
{
    int T,k;
    scanf("%d",&T);
    for(k=1;k<=T;k++)
    {
        scanf("%d %d",&m,&n);
        getchar();
        int i,j;
        for(i=0;i<m;i++)
        {
            gets(map[i]);
        }
      /*  for(i=0;i<m;i++)
        {
            puts(map[i]);
        }*/


        nx=ny=0;
        memset(xs,0,sizeof(xs));
        memset(ys,0,sizeof(ys));
        int flag;


        for(i=0;i<m;i++)  //横向编号
        {
            flag=0;
            for(j=0;j<n;j++)
            {
                if(map[i][j]=='o')
                {
                    if(flag!=1) nx++;
                    flag=1;
                    xs[i][j]=nx;
                }
                else if(map[i][j]=='#') flag=0;
            }
        }
        for(j=0;j<n;j++) //纵向编号
        {
            flag=0;
            for(i=0;i<m;i++)
            {
                if(map[i][j]=='o')
                {
                    if(flag!=1) ny++;
                    flag=1;
                    ys[i][j]=ny;
                }
                else if(map[i][j]=='#') flag=0;
            }
        }
       /*x,y方向的编号分别为二分图一边点*/
        for(i=0;i<=nx;i++) g[i].clear();


        for(i=0;i<m;i++)
        {
            for(j=0;j<n;j++)
            {
                if(xs[i][j]&&ys[i][j])  //两个编号有交叉,,
                {
                    g[xs[i][j]].push_back(ys[i][j]);  //有关系
                }
            }
        }


        printf("Case :%d\n",k);
        printf("%d\n",hungary());
    }
    return 0;
}




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值