CF1015E2 星星阵
1 CF1015E2 星星阵
2 题目描述
时间限制 \(3s\) | 空间限制 \(256M\)
星星的图形是由中心的星号字符 \(*\) 和相同长度的四条射线(左、右、上、下)构成。星星的大小是其射线的长度。星星的大小必须是正数(即长度不能为 \(0\))。空白位置用 \(.\) 表示。例如下面的图形:
最左边的图形是大小为 \(1\) 的星星,中间的图形是大小为 \(2\) 的星星,最右边的图形是大小为 \(3\) 的星星。给你一个 \(𝑛*𝑚\) 的只包含星号 \(*\) 和点 \(.\)的矩形。行的编号从 \(1\) 到 \(𝑛\),列的编号从 \(1\) 到 \(𝑚\)。如果可能的话使用任意数量的星星绘制这个网格。星星可以相互交叉、重叠甚至重合。输出的星星个数不能超过 \(𝑛*𝑚\)。每颗星都应该完全在网格内。可以使用相同大小和任意大小的星星。
3 题解
对于这道题,我们可以考虑一个时间复杂度略优于 \(O(n^3)\) 的算法:枚举所有 \(*\) 号,对于每个 \(*\) 号,我们枚举其上下左右同时延伸的情况下能延伸到的最远距离,并记录下这个星星和其覆盖的点。如果所有 \(*\) 号都被覆盖,说明星星可以覆盖所有的 \(*\) 号,否则不可以,输出 \(-1\)。由于这种算法中 \(*\) 号的个数可能到达 \(n^2\),而上下左右同时延伸的最坏情况约为 \(O(n)\),所以这个算法看上去是 \(O(n^3)\) 的,但事实真的如此吗?
我们发现:在边界时,我们枚举上下左右延伸的情况的那层循环实际上并不会跑满,而只会跑很小的一点。例如,在边界上只会跑 \(O(1)\) 的时间复杂度,就会因为不合法而退出。随着时间复杂度随着慢慢层数增加而增加,更深层的节点数慢慢减少。最终的时间复杂度略微高于 \(O(n^2)\),数量级大致在 \(10^7 - 10^8\)。由于这道题给了足足三秒,应该是可以跑过去的。
具体细节方面:我们可以利用一个 \(pair\) 队列将所有的 \(*\) 号存储起来,既方便查询元素又方便删除元素。存储答案时也类似,用 \(pair\) 队列存储当前星星的中心与向四周延伸的长度,最终输出答案时直接访问队头元素即可。用 \(bool\) 类型的数组存储每个点是否被覆盖,在输出前将所有的 \(*\) 号遍历一遍,如果有任意一个 \(*\) 号没有被覆盖到则直接输出 \(-1\)。
4 代码(空格警告):
#include <iostream>
#include <queue>
#include <cstdio>
using namespace std;
const int N = 1005;
int n, m, x, y;
string s;
char Map[N][N];
bool fMap[N][N];
queue< pair<int, int> > q;
queue< pair< pair<int, int>, int> > ans;
bool check(int dx, int dy)
{
if (dx < 1 || dx > n || dy < 1 || dy > m || Map[dx][dy] != '*') return 0;
return 1;
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
{
cin >> s;
for (int j = 1; j <= m; j++)
{
Map[i][j] = s[j-1];
if (Map[i][j] == '*') q.push(make_pair(i, j));
}
}
while (!q.empty())
{
x = q.front().first;
y = q.front().second;
q.pop();
for (int i = 1; i <= n; i++)
{
if (i == 2) fMap[x][y] = 1;
if (check(x-i, y) && check(x+i, y) && check(x, y+i) && check(x, y-i))
{
fMap[x-i][y] = 1;
fMap[x+i][y] = 1;
fMap[x][y+i] = 1;
fMap[x][y-i] = 1;
}
else if (i != 1)
{
ans.push(make_pair(make_pair(x, y), i-1));
break;
}
else break;
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (Map[i][j] == '*' && fMap[i][j] == 0)
{
puts("-1");
return 0;
}
}
}
printf("%lu\n", ans.size());
while (!ans.empty())
{
printf("%d %d %d\n", ans.front().first.first, ans.front().first.second, ans.front().second);
ans.pop();
}
return 0;
}