ZJU 1654 Place the Robots(二分图)

Just_ACM 2010-04-16 12:21:38
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 ))
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;
}





通过数据测试,但本人很遗憾,无法ac,如果有哪位大神能看出其中的差错,或给出一组反例数据(算法绝对没有错,可以网上搜索相关的PPT,这是一道经典题,可惜本人无法将其ac,网上目前还搜索不到相关ac的代码),请告诉我,本人万分感谢!
...全文
159 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
Just_ACM 2010-04-18
  • 打赏
  • 举报
回复
问题解决了,就是那个原因,之前的程序有问题是因为我自己,前面还改了东西,没有改回来,呵呵,粗心了。
现在把正确的代码再发一遍
#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;
}



Just_ACM 2010-04-17
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 logiciel 的回复:]
以下可能是一个反例:

1
5 5
ooooo
o*##o
o#o#o
o##*o
ooooo

LZ程序输出:

Case :1
7
[/Quote]

根据你给的数据,我找到了问题,我忽略了横块和竖块之间虽然有共同块,但该共同块确实草地,不能放robot,所以将其可能性排除,因此,我做了如下修改, 得出的答案是 5 :[code=C/C++]
]#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] != 'o')
{
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] != 'o')
{
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] != 'o')
{
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] != 'o')
{
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;
}
[/code

在构造邻接矩阵的地方,多加了一个条件,判断是否为‘o’, 但悲剧的事情发生了,还是不能ac。。。继续wa, 希望各位大侠再帮帮小弟。。。感激不尽!!!

Just_ACM 2010-04-17
  • 打赏
  • 举报
回复
谢谢, 我用你的数据找到了错的地方,我没有把草地不能放 机器人的条件考虑进去。。万分感谢!
soswaidao 2010-04-16
  • 打赏
  • 举报
回复
友情帮顶
logiciel 2010-04-16
  • 打赏
  • 举报
回复
以下可能是一个反例:

1
5 5
ooooo
o*##o
o#o#o
o##*o
ooooo

LZ程序输出:

Case :1
7
菜鸟多少号? 2010-04-16
  • 打赏
  • 举报
回复
接分 帮你顶上

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧