Codevs 1564 打地鼠

来源:互联网 发布:承德医学院网络教育 编辑:程序博客网 时间:2024/06/16 09:37
题目描述 Description

打地鼠是这样的一个游戏:地面上有一些地鼠洞,地鼠们会不时从洞里探出头来很短时间后又缩回洞中。玩家的目标是在地鼠伸出头时,用锤子砸其头部,砸到的地鼠越多分数也就越高。

游戏中的锤子每次只能打一只地鼠,如果多只地鼠同时探出头,玩家只能通过多次挥舞锤子的方式打掉所有的地鼠。你认为这锤子太没用了,所以你改装了锤子,增加了锤子与地面的接触面积,使其每次可以击打一片区域。如果我们把地面看做m*n的方阵,其每个元素都代表一个地鼠洞,那么锤子可以覆盖R*C区域内的所有地鼠洞。但是改装后的锤子有一个缺点:每次挥舞锤子时,对于这R*C的区域中的所有地洞,锤子会打掉恰好一只地鼠。也就是说锤子覆盖的区域中,每个地洞必须至少有1只地鼠,且如果某个地洞中地鼠的个数大于1,那么这个地洞只会有1只地鼠被打掉,因此每次挥舞锤子时,恰好有R*C只地鼠被打掉。由于锤子的内部结构过于精密,因此在游戏过程中你不能旋转锤子(即不能互换R和C)。

你可以任意更改锤子的规格(即你可以任意规定R和C的大小),但是改装锤子的工作只能在打地鼠前进行(即你不可以打掉一部分地鼠后,再改变锤子的规格)。你的任务是求出要想打掉所有的地鼠,至少需要挥舞锤子的次数。

Hint:由于你可以把锤子的大小设置为1*1,因此本题总是有解的。

输入描述 Input Description

第一行包含两个正整数m和n;

下面m行每行n个正整数描述地图,每个数字表示相应位置的地洞中地鼠的数量。

输出描述 Output Description

输出一个整数,表示最少的挥舞次数。

本题如果进行暴力枚举,100*100的格子,锤子也是100*100,100的四次方肯定会超时。所以我们需要推一些有用的小结论。 

1. 观察一下左上角会发现左上角第一个点必须被以它为左上角的锤子敲击他上面数字的次数,以此递推,从左往右再从上到下,左上角那个点就敲击剩下的次数,如果发现有负数,就直接break掉,测试其他的锤子。  

2. 如果只有优化1,还是不能AC。这时会发现当一种情况(a,b),被找出不能打完地鼠时,那么(k1*a,k2*b)情况下的锤子也不能打完地鼠。证明方法可从结论1中推出,因为(a,b) 时已经覆盖了被减成负数的点(x,y),那么锤子覆盖按倍数增大的情况,就相当于多个相同的锤子进行敲击,答案也会是负数甚至更糟。 

 加上上面两个结论,就可以AC了。  

下附AC 代码。

#include<iostream>#define maxn 105using namespace std;long long a[maxn][maxn];long long c[maxn][maxn];long long judge[maxn][maxn];long long m,n,sum=0,ans=9999999999;int main(){cin>>m>>n;for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){cin>>a[i][j];sum+=a[i][j];}for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){if(judge[i][j]==0){for(int k=1;k<=m;k++)for(int o=1;o<=n;o++)c[k][o]=a[k][o];bool flag=true;for(int k=1;k<=m;k++)if(flag)for(int o=1;o<=n;o++)if(flag)if(c[k][o]>0){long long jian=c[k][o];for(int x=k;x<=k+i-1;x++)if(flag)for(int y=o;y<=o+j-1;y++)if(flag){c[x][y]-=jian;if(c[x][y]<0) flag=false;}}if(flag==false){for(int k=i;k<=m;k+=i)for(int o=j;o<=n;o+=j){judge[k][o]=1;}}else{ans=min(ans,sum/i/j);}}}cout<<ans<<endl;}