P2484 [SDOI2011]打地鼠 题解

P2484 [SDOI2011]打地鼠 题解

题目

P2484 [SDOI2011]打地鼠 题解

分析

锤子每次只能将每个洞里打掉一只地鼠,所以对于每次击打,都会打掉 r×c 只地鼠。而每次击打的范围内的每个洞里必须都要有地鼠,所以地鼠的总数必须被 r×c 整除。

因为每只地鼠都需要被干掉,所以如果当前方案成立,总可以通过不断在地图左上角寻找有地鼠的洞,再以这个洞为锤子击打的范围的左上角进行击打来打完所有地鼠。

而如果在打地鼠的时候发现击打范围内有已经没有地鼠的洞或者会打到地图外面的话,当前方案就不成立。

思路

由于地图只是个 100×100 的矩形,所以我们完全可以枚举 rc,只要锤子覆盖的区域大小即 r×c 能整除地图中的地鼠总数就进行尝试。

根据上文的分析,我们只需要在整个地图左上角找到一个还剩的有地鼠的洞,设这个洞的坐标为 (x,y),然后进行以下操作:

  1. 判断 (x+r1,y+c1) 是否在地图外面。在的话说明这个锤子不满足题意,结束,否则进行下一步。
  2. 判断(x,y)(x+r1,y+c1) 范围内有没有空地鼠洞。如果有的话,说明这个锤子不满足题意,结束,否则进行下一步。
  3. (x,y)(x+r1,y+c1) 范围内的地鼠数量减一。
  4. 将这个锤子的击打总次数加一。
  5. 回到第 1 步操作直到所有地鼠洞为空。

因为 (x+y) 的地鼠在锤子大小满足题意的情况下再怎么样都会被打完,所以我们也可以直接将 (x,y)(x+r1,y+c1) 范围内的地鼠数量减 (x,y) 里的地鼠数量(相当于连续打 (x,y) 里的地鼠数量次),判断减之后有没有为负的洞,有说明锤子不满足题意,没有的话将锤子总击打次数加上 (x,y) 里的地鼠数量,然后继续尝试。这算是个小优化。

最后就输出所有可行的锤子中击打次数最小的值就好了。更详细的解释请参阅代码里的注释。

代码

#pragma GCC optimize(2)
#include <cstdio>
using namespace std;
int n, m, tot = 0;
long long ans = 1145141919810;
int maze[105][105];
int tmaze[105][105]; //因为要尝试多次且每次尝试都会修改地鼠洞内地鼠的数量,
                     //我们需要在每次尝试前复制原始的地图
long long Min(long long a, long long b)
{
    return a > b ? b : a;
}
inline bool findfirst(int &x, int &y) //从地图左上角找到第一个有地鼠的地鼠洞,返回是否找到
{
    for (register int i = 1; i <= n; i++)
    {
        for (register int j = 1; j <= m; j++)
        {
            if (tmaze[i][j])
            {
                x = i, y = j; //找到就将这个洞的坐标传回去
                return 1;
            }
        }
    }
    return 0;
}
inline void dfs(int r, int c, int cnt) //参数表示锤子大小和花费
{
    int x, y;             //用x,y表示目前在左上角找到的地鼠洞为(x,y)
    if (!findfirst(x, y)) //如果没找到有地鼠的洞说明找完了,更新答案并结束
    {
        ans = Min(ans, 1ll * cnt);
        return;
    }
    if (x + r - 1 > n || y + c - 1 > m || cnt >= ans) //操作1,如果要打左上角的地鼠
                                                      //洞锤子会超过地图,说明这组
                                                      //方案不成立
        return;
    int red = tmaze[x][y]; //操作3,将(x,y)到(x+r-1,y+c-1)内的地鼠数量都减去(x,y)内的地鼠数
    for (register int i = x; i <= x + r - 1; i++)
    {
        for (register int j = y; j <= y + c - 1; j++)
        {
            tmaze[i][j] -= red;
            if (tmaze[i][j] < 0) //操作2,如果这个洞里的地鼠数量为负说明这组方案不成立
                return;
        }
    }
    dfs(r, c, cnt + red); //操作4、5,加上操作次数,继续打剩下的地鼠
    return;
}
void cpymaze()
{
    for (register int i = 1; i <= n; i++)
        for (register int j = 1; j <= m; j++)
            tmaze[i][j] = maze[i][j]; //复制原始的地图
}
signed main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            scanf("%d", &maze[i][j]);
            tot += maze[i][j]; //记录地鼠总数
        }
    }
    for (int r = 1; r <= n; r++)
    {
        for (int c = 1; c <= m; c++)
        {
            if (tot % (r * c) == 0) //分析中所说的,只有锤子的覆盖大小能整除地鼠总数才行
            {
                cpymaze();
                dfs(r, c, 0);
            }
        }
    }
    printf("%lld\n", ans);
}
posted @   if-OF  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示