ZJU 1654 Place the Robots(二分图)

本文介绍了一种利用二分图解决机器人在地图上放置的问题。通过合并空地形成大块,并构造邻接矩阵来寻找最大匹配数量,以此确定最多能放置多少个机器人。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ZJU 1654 Place the Robots(二分图)

 

 

Robert is a famous engineer. One day he was given a task by his boss. The background of the task was the following:

 

Given a map consisting of square blocks. There were three kinds of blocks: Wall, Grass, and Empty. His boss wanted to place as many robots as possible in the map. Each robot held a laser weapon which could shoot to four directions (north, east, south, west) simultaneously. A robot had to stay at the block where it was initially placed all the time and to keep firing all the time. The laser beams certainly could pass the grid of Grass, but could not pass the grid of Wall. A robot could only be placed in an Empty block. Surely the boss would not want to see one robot hurting another. In other words, two robots must not be placed in one line (horizontally or vertically) unless there is a Wall between them.

 

Now that you are such a smart programmer and one of Robert's best friends, He is asking you to help him solving this problem. That is, given the description of a map, compute the maximum number of robots that can be placed in the map.

 

 

Input

 

The first line contains an integer T (<= 11) which is the number of test cases.

 

For each test case, the first line contains two integers m and n (1<= m, n <=50) which are the row and column sizes of the map. Then m lines follow, each contains n characters of '#', '*', or 'o' which represent Wall, Grass, and Empty, respectively.

 

 

Output

 

For each test case, first output the case number in one line, in the format: "Case :id" where id is the test case number, counting from 1. In the second line just output the maximum number of robots that can be placed in that map.

 

 

Sample Input

 

2

4 4

o***

*###

oo#o

***o

4 4

#ooo

o#oo

oo#o

***#

 

 

Sample Output

 

Case :1

3

Case :2

5

 

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=654

 

 

 

 

 

算法:不要用搜索来解这道题,可以灵活运用二分图的想法,构造邻接矩阵。。将横着的没有被阻挡的块合并成一个大块。将竖着的没有被阻挡的块合并成一个大块。这样得到若干个横向块,和纵向块,如果一横向块与纵向块之间有共同的点,那么就将这两个大块连线(构造一个横向块与纵向块的邻接矩阵)。构造好之后就可以算横向块与纵向块的最大匹配数。这个数就是机器人的可以摆放的个数。。

 

 

  

代码:

 

 

 #include <iostream>
using namespace std;

struct XBlock
{
 int x;
 int starty, endy;
};

struct YBlock
{
 int y;
 int startx, endx;

};

XBlock bx[2501];
YBlock yb[2501];
char map[51][51];
char temp[51][51];
int casen, n, m, bxn, byn, match;
bool connect[2501][2501];
bool state[1501];
int res[1501];

void copy()
{
 int i,j;
 for( i = 1; i <= n ; i++)
  for( j = 1; j <= m; j++)
   temp[i][j] = map[i][j];

}
void MakeXBlock()
{
 int i, j, t;
 bxn = 0; //记录bx[]的下标
 for( i = 1; i <= n ; i++)
  for( j = 1; j <= m; j++)
   if(temp[i][j] == 'o')
   {
    bxn ++;
    bx[bxn].x = i;
    for( t = j; t > 0; t--)//确定横向块的起始坐标
    {
     if(temp[i][t] == '#')
     {
      bx[bxn].starty = t+1;
      break;
     }
     else
     {
      bx[bxn].starty = t;
      if(temp[i][t] == 'o' && t != j) //删除这一块中的其他‘o’
       temp[i][t] = 'x';
     }
    }

    for( t = j; t <= m; t++ )//确定横向块的结束坐标
    {
     if(temp[i][t] == '#')
     {
      bx[bxn].endy = t-1;
      break;
     }
     else
     {
      bx[bxn].endy = t;
      if(temp[i][t] == 'o' && t != j )  //删除这一块中的其他‘o’
       temp[i][t] = 'x';
     }
    }

   }
}

void MakeYBlock()
{
 int i, j, t;
 byn = 0;
 for(j = 1; j <= m ;j++)
  for(i = 1; i <= n; i++)
   if(temp[i][j] == 'o')
   {
    byn++;
    yb[byn].y = j;
    for(t = i ; t > 0 ; t--) //确定纵向块的起始坐标
    {
     if(temp[t][j] == '#')
     {
      yb[byn].startx  = t+1;
      break;
     }
     else
     {
      yb[byn].startx = t;
      if(temp[t][j] == 'o' && t != i) //删除这一块中的其他‘o’
       temp[t][j] = 'x';
     }
    }

    for( t = i; t <= n; t++ )//确定纵向快的结束坐标
    {
     if(temp[t][j] == '#')
     {
      yb[byn].endx  = t-1;
      break;
     }
     else
     {
      yb[byn].endx  = t;
      if(temp[t][j] == 'o' && t != i ) //删除这一块中的其他‘o’
       temp[t][j] = 'x';
     }
    }
   }
}
bool find(int i)    //匈牙利算法,进行最大匹配计算
{
 int j;
 for(j = 1; j <= byn; j++)
  if(connect[i][j] == true && state[j] == false)
  {
   state[j] = true;
   if(res[j] == 0 || find (res[j]) == true)
   {
    res[j] = i;
    return true;
   }
  }
  return false;
}


int main()
{
 cin>>casen;
 int i, j, num = casen;
 while(num--)
 {
  cin>>n>>m;
  for( i = 1; i <= n ; i++)
   for( j = 1; j <= m; j++)
    cin>>map[i][j];

  for( i = 1; i <= 1500; i++) // 初始化结构体里的坐标
  {
   bx[i].endy = bx[i].starty = bx[i].x = 0;
   yb[i].endx = yb[i].startx = yb[i].y = 0;
  }
  copy();
  MakeXBlock();//构造横向块
  copy();
  MakeYBlock(); //构造纵向块
  memset(connect, false, sizeof(connect));
  memset(res, 0 , sizeof(res));
  for(i = 1; i <= bxn; i++) //构造邻接矩阵
   for(j = 1; j <= byn; j++)
   {
    if((bx[i].x >= yb[j].startx && bx[i].x <= yb[j].endx) &&( yb[j].y >= bx[i].starty && yb[j].y <= bx[i].endy ) && map[bx[i].x][yb[j].y] == 'o')
     connect[i][j] = true;
   }
  match = 0;
  for(i = 1; i <= bxn; i++) //计算最大匹配数
  {
   memset(state, false, sizeof(state));
   if(find(i))
    match++;
  }
  cout<<"Case :"<<casen- num<<endl;
  cout<<match<<endl;

 }
 return 0;
}

 

不要忽略了横块和竖块之间虽然有共同块,但该共同块却是草地,不能放robot,所以要将其可能性排除

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值