Description
打地鼠是这样的一个游戏:地面上有一些地鼠洞,地鼠们会不时从洞里探出头来很短时间后又缩回洞中。玩家的目标是在地鼠伸出头时,用锤子砸其头部,砸到的地鼠越多分数也就越高。
游戏中的锤子每次只能打一只地鼠,如果多只地鼠同时探出头,玩家只能通过多次挥舞锤子的方式打掉所有的地鼠。你认为这锤子太没用了,所以你改装了锤子,增加了锤子与地面的接触面积,使其每次可以击打一片区域。如果我们把地面看做m*n的方阵,其每个元素都代表一个地鼠洞,那么锤子可以覆盖R*C区域内的所有地鼠洞。但是改装后的锤子有一个缺点:每次挥舞锤子时,对于这的R*C区域中的所有地洞,锤子会打掉恰好一只地鼠。也就是说锤子覆盖的区域中,每个地洞必须至少有1只地鼠,且如果某个地洞中地鼠的个数大于1,那么这个地洞只会有1只地鼠被打掉,因此每次挥舞锤子时,恰好有R*C只地鼠被打掉。由于锤子的内部结构过于精密,因此在游戏过程中你不能旋转锤子(即不能互换R和C)。
你可以任意更改锤子的规格(即你可以任意规定R和C的大小),但是改装锤子的工作只能在打地鼠前进行(即你不可以打掉一部分地鼠后,再改变锤子的规格)。你的任务是求出要想打掉所有的地鼠,至少需要挥舞锤子的次数。
Hint:由于你可以把锤子的大小设置为1*1,因此本题总是有解的。
Input
第一行包含两个正整数m和n;
下面m行每n行个正整数描述地图,每个数字表示相应位置的地洞中地鼠的数量。
Output
输出一个整数,表示最少的挥舞次数。
Sample Input
3 3
1 2 1
2 4 2
1 2 1
Sample Output
4
Hint
【样例说明】
使用2*2的锤子,分别在左上、左下、右上、右下挥舞一次。
对于30%的数据,m,n<=5;
对于60%的数据,m,n<=30;
对于100%的数据,1<=m,n<=100,其他数据不小于0,不大于10^5。
【分析】
可以分析得出,答案是具有单调性的。锤子的面积越大,则答案越小。那么我们只需生成所有可能的锤子面积,排序后从大到小枚举验证。第一个满足条件则输出。
优化:sum表示所有点的权值和,我们只验证可以整除sum的锤子面积。答案为 sum/s, s表示面积。
验证:通过贪心验证,对于当前点,若不剩老鼠了则讨论下一个点。若剩,则将以它为左上角的矩形权值都减少它的权值。在验证时,如果以它为左上角的矩形超出了边界,return false。
【代码】
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
int x,y,s;
}Num[20005];
int N,M,sum,top;
int Map[105][105],G[105][105];
void _init()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
{
scanf("%d",&Map[i][j]);
sum+=Map[i][j];
}
}
bool _cmp(node A,node B)
{
return A.s>B.s;
}
void _next(int &x,int &y)
{
if(y<M)
y++;
else
{
x++;
y=1;
}
}
bool _check(int R,int C)
{
int t,x,y;
memcpy(G,Map,sizeof(Map));
x=y=1;
while(x<=N&&y<=M)
{
if(G[x][y]==0)
{
_next(x,y); //下一个点
continue;
}
t=G[x][y];
if(x+R-1>N) return false; //矩形超出边界
if(y+C-1>M) return false;
for(int i=x;i<=x+R-1;i++) //减少权值
for(int j=y;j<=y+C-1;j++)
{
if(G[i][j]>=t)
G[i][j]-=t;
else
return false;
}
}
return true;
}
void _solve()
{
for(int i=1;i<=N;i++) //生成所有可能
for(int j=1;j<=M;j++)
{
Num[++top].x=i;
Num[top].y=j;
Num[top].s=i*j;
}
sort(Num+1,Num+top+1,_cmp); //排序
for(int i=1;i<=top;i++)
if(sum%Num[i].s==0) //只验证能整除的
if(_check(Num[i].x,Num[i].y))
{
printf("%d\n",sum/Num[i].s);
return;
}
}
int main()
{
_init();
_solve();
return 0;
}