第1题 宫廷守卫(guards.pas)
【问题描述】
从前有一个王国,这个王国的城堡是一个矩形,被分为M×N个方格。一些方格是墙,而另一些是空地。这个王国的国王在城堡里设了一些陷阱,每个陷阱占据一块空地。
一天,国王决定在城堡里布置守卫,他希望安排尽量多的守卫。守卫们都是经过严格训练的,所以一旦他们发现同行或同列中有人的话,他们立即向那人射击。因此,国王希望能够合理地布置守卫,使他们互相之间不能看见,这样他们就不可能互相射击了。守卫们只能被布置在空地上,不能被布置在陷阱或墙上,且一块空地只能布置一个守卫。如果两个守卫在同一行或同一列,并且他们之间没有墙的话,他们就能互相看见。(守卫就像象棋里的车一样)
你的任务是写一个程序,根据给定的城堡,计算最多可布置多少个守卫,并设计出布置的方案。
【输入】
第一行两个整数M和N(1≤M,N≤200),表示城堡的规模。
接下来M行N列的整数,描述的是城堡的地形。第i行j列的数用ai,j表示。
ai,j=0,表示方格[i,j]是一块空地;
ai,j=1,表示方格[i,j]是一个陷阱;
ai,j=2,表示方格[i,j]是墙。
【输出】
第一行一个整数K,表示最多可布置K个守卫。
此后K行,每行两个整数xi和yi,描述一个守卫的位置。
【样例】
guards.in guards.out
34 2
20 0 0 12
22 2 1 33
01 0 2
样例数据如图5-2(黑色方格为墙,白色方格为空地,圆圈为陷阱,G表示守卫)
|
|
|
|
|
|
| ○ |
| ○ |
|
|
| G |
|
|
|
|
| ○ |
| ○ | G |
|
这道题标准解法是二分图最大匹配,我用的网络流也过了,实现起来要麻烦一点,速度要慢一点。
标准解法。把所有的横着的连序的块block和竖着的block抽象成点,分为横点和竖点的两个子图,
两点的连边就确定了一个守卫的位置,然后求最大匹配,这个建图方法比较经典。
我的方法和标准方法比较接近,但是绕了点弯子,就是
这个方法,求一次最大流,最后输出方案的时候弄错了。
我只考虑了与每一个点相连的竖集合到点的残量为零,实际上既要相连的竖集合,还要相连的横集合残量都为零,才能表示守卫的位置。
当时想了很久,但是想起来,跟我们最近做的题相比,这道题确实是很基础
#include <cstdio>
long n;long m;
long map[210][210];
struct node
{
long x;
long y;
};
node block[120010];
long size = 0;
const long inf = 0x7fff0000;
long s;long t;
long vd[120010];
long d[120010];
struct node2
{
long i;
long f;
long sol;
node2* b;
node2* next;
};
node2* head[120010];
inline long min(long a,long b)
{
if (a < b) return a;
return b;
}
long sap(long u,long flow)
{
if (u == t)
{
return flow;
}
long count = 0;
for (node2* vv=head[u];vv;vv=vv->next)
{
long v = vv->i;
if (d[u]-d[v]==1)
{
long f = vv->f;
long tmp = sap(v,min(flow-count,f));
count += tmp;
vv->sol += tmp;
vv->b->sol -= tmp;
vv->f -= tmp;
vv->b->f += tmp;
if (count == flow)
{
return count;
}
}
}
if (d[s]>=size)
{
return count;
}
vd[d[u]]--;
if (vd[d[u]]==0)
{
d[s] = size;
}
d[u]++;
vd[d[u]]++;
return count;
}
void insert(long a,long b,long c)
{
node2* nn = new node2;
nn->i = b;
nn->f = c;
nn->next = head[a];
nn->sol = 0;
head[a] = nn;
}
int main()
{
freopen("guards.in","r",stdin);
freopen("guards.out","w",stdout);
scanf("%ld%ld",&n,&m);
size = 2;s = 1;t = 2;
for (long i=1;i<n+1;i++)
{
for (long j=1;j<m+1;j++)
{
scanf("%ld",&map[i][j]);
if (map[i][j]==0)
{
size ++;
block[size].x = i;
block[size].y = j;
map[i][j] = size;
}
else if (map[i][j]==1)
map[i][j] = 0;
else map[i][j] = -inf;
}
}
for (long i=1;i<n+1;i++)
{
for (long j=1;j<m+1;j++)
{
if (map[i][j]>0)
{
size ++;
insert(s,size,1);
insert(size,s,0);
head[s]->b = head[size];
head[size]->b = head[s];
while (map[i][j]>=0&&j<m+1)
{
if (map[i][j]>0)
{
insert(size,map[i][j],inf);
insert(map[i][j],size,0);
head[size]->b = head[map[i][j]];
head[map[i][j]]->b = head[size];
}
j++;
}
j--;
}
}
}
for (long j=1;j<m+1;j++)
{
for (long i=1;i<n+1;i++)
{
if (map[i][j]>0)
{
size ++;
insert(size,t,1);
insert(t,size,0);
head[t]->b = head[size];
head[size]->b = head[t];
while (map[i][j]>=0&&i<n+1)
{
if (map[i][j]>0)
{
insert(map[i][j],size,inf);
insert(size,map[i][j],0);
head[size]->b = head[map[i][j]];
head[map[i][j]]->b = head[size];
}
i++;
}
i--;
}
}
}
vd[0] = size;
long ans = 0;
while (d[s]<size)
{
ans += sap(s,inf);
}
printf("%ld\n",ans);
for (node2* uu=head[s];uu;uu=uu->next)
{
long u = uu->i;
for (node2* vv=head[u];vv;vv=vv->next)
{
long v = vv->i;
if (vv->sol==1&&head[v]->sol==1)
{
printf("%ld %ld\n",block[v].x,block[v].y);
}
}
}
return 0;
}