题目
在一块平原上有一头大象。
平原被分成n×mn×m个格子。初始时大象位于(1,1)(1,1)。每一秒,大象会移动到一个相邻的格子上(四连通),但不会移动到平原外面。由于你视力不好,你无法知道大象每次移动到哪个格子上。
你可以使用火球术来攻击地面。每次释放火球术,你可以攻击任意多个格子。每个格子只能被攻击一次。大象不能移动到被攻击过的格子上,如果大象相邻的格子都无法移动,那么它会停在原地不动。
你的目标是最后只剩下一个格子没有被火球术攻击过,然后你去那里活捉大象。在此之前,你必须保证你的火球术不会击中大象。由于大象的战斗力十分强大,只有在某些格子中你才能抓住大象。
然而,由于你魔法水平不高,在使用火球术前你需要进行蓄势。第一次蓄势的时间至少为n+mn+m秒,接下来每次蓄势时间都要比上次长。蓄势时间必须是整数秒。你希望最小化最后一次的蓄势时间。
注意:每一秒大象先移动,接着你才会释放火球术。也就是说,如果你第一次的蓄势时间为n+mn+m秒,那么大象已经移动了n+mn+m次。
Input
第一行包含两个整数 n,mn,m。
接下来 nn 行每行 mm 个数,每个数只能是 0 或 1。第 i+1i+1 行第 jj 列的数表示 在(i,j)(i,j)这个格子中,你能否抓住大象(1 表示能,0 表示不能)。
Output
输出若干行,每行表示释放一次火球术。
每行第一个整数表示蓄势时间,接下来若干个整数表示攻击的格子,第 ii 行第 jj 列的格子编号为(i−1)∗m+j(i−1)∗m+j。每行最后输出一个 0。
如果无解输出一行一个整数−1−1。
Examples
3 3
1 1 1
1 1 1
1 1 1
7 1 3 7 9 0
9 2 4 6 8 0
Notes
alt
特殊条件A:在所有格子中,你都能抓住大象。
特殊条件B:至少存在一个格子,你可以抓住大象。
对于每个测试点,如果这个测试点无解并且你判断对了,得5分。
如果这个测试点有解,记这个测试点最优策略的答案为ansans:
如果你的答案=ans=ans,得5分;
如果你的答案≤ans+2≤ans+2,得4分;
如果你的答案≤ans+4≤ans+4,得3分;
如果你的答案≤2ans≤2ans,得2分;
如果你的答案≤108≤108,得1分。
满足多个条件取最高分。
思路
对格子进行黑白染色,那么由步数的奇偶可以推断出大象所在格
子的颜色,然后删去另一种颜色的格子,但要保证删去之后,剩
余的格子依旧联通。
我们将这个过程反过来,一开始只有一个格子,每次增加若干个
同样颜色的格子,同时保证联通。由于我们想让步数尽量少,因
此我们会一圈一圈往外加。从一个格子加满整个棋盘的步数,就
是它到四个顶点的曼哈顿距离的最大值。
注意到除了第一次以外,之后的每一次蓄势时间都是奇数。假设
(1, 1) 为黑,那么第一次删黑格就是奇数,删白格就是偶数。当
n, m 都是偶数时,如果两种方法步数一样,但第一种方法第一次
删黑格,第二种方法第一次删白格,那么第二种方法最终相当于
少了一步。
时间复杂度 O(nm)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1077;
int n,m,x,y,cc=0;
vector<int> ans[N*2];
int solve(int x,int y){return max(x-1,n-x)+max(y-1,m-y);}
int main()
{
scanf("%d%d",&n,&m);x=-1;y=-1;int k=(n+m)&1;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
{
int o,t=solve(i,j);scanf("%d",&o);if(!o)continue;
if((x==-1&&y==-1)||t<solve(x,y)||(t==solve(x,y)&&(((i+j)&1)^(t&1))!=k))x=i,y=j;
}
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)ans[abs(i-x)+abs(j-y)].push_back(++cc);
int p=n+m,ff=1;
for(int i=n+m;i;i--) if(ans[i].size())
{
if(ff)
{
ff=0;p+=((x+y+i)&1)^k^1;
printf("%d ",p);if(!(p&1))p--;
}
else{p+=2;printf("%d ",p);}
for(int j=0;j<ans[i].size();j++)printf("%d ",ans[i][j]);
puts("0");
}
}