【SDOI 2011】打地鼠

来源:互联网 发布:数组求平均值 编辑:程序博客网 时间:2024/04/27 14:28

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 31 2 12 4 21 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;elsereturn 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;}


原创粉丝点击