1. 程式人生 > >2241. [SDOI2011]打地鼠【暴力+剪枝】

2241. [SDOI2011]打地鼠【暴力+剪枝】

ret 一次 位置 剪枝 class medium namespace 部分 script

Description

打地鼠是這樣的一個遊戲:地面上有一些地鼠洞,地鼠們會不時從洞裏探出頭來很短時間後又縮回洞中。玩家的目標是在地鼠伸出頭時,用錘子砸其頭部,砸到的地鼠越多分數也就越高。

遊戲中的錘子每次只能打一只地鼠,如果多只地鼠同時探出頭,玩家只能通過多次揮舞錘子的方式打掉所有的地鼠。你認為這錘子太沒用了,所以你改裝了錘子,增加了錘子與地面的接觸面積,使其每次可以擊打一片區域。如果我們把地面看做M*N的方陣,其每個元素都代表一個地鼠洞,那麽錘子可以覆蓋R*C區域內的所有地鼠洞。但是改裝後的錘子有一個缺點:每次揮舞錘子時,對於這R*C的區域中的所有地洞,錘子會打掉恰好一只地鼠。也就是說錘子覆蓋的區域中,每個地洞必須至少有1

只地鼠,且如果某個地洞中地鼠的個數大於1,那麽這個地洞只會有1只地鼠被打掉,因此每次揮舞錘子時,恰好有R*C只地鼠被打掉。由於錘子的內部結構過於精密,因此在遊戲過程中你不能旋轉錘子(即不能互換RC)。

你可以任意更改錘子的規格(即你可以任意規定RC的大小),但是改裝錘子的工作只能在打地鼠前進行(即你不可以打掉一部分地鼠後,再改變錘子的規格)。你的任務是求出要想打掉所有的地鼠,至少需要揮舞錘子的次數。

Hint:由於你可以把錘子的大小設置為1*1,因此本題總是有解的。

Input

第一行包含兩個正整數MN

下面M行每行N個正整數描述地圖,每個數字表示相應位置的地洞中地鼠的數量。

Output

輸出一個整數,表示最少的揮舞次數。

Sample Input

3 3

1 2 1

2 4 2

1 2 1

Sample Output

4
【樣例說明】

使用2*2的錘子,分別在左上、左下、右上、右下揮舞一次。

【數據規模和約定】
對於100%的數據,1<=M,N<=100,其他數據不小於0,不大於10^5

數據太水,枚舉長寬然後check的時候加個特判就快的飛起

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int a[105][105],b[105][105],n,m,sum,ans=0x7fffffff;
bool check(int x,int y)
{
	if (sum%(x*y)!=0) return false;
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			b[i][j]=a[i][j];
	
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			if (i+x-1<=n && j+y-1<=m)
			{
				int num=b[i][j];
				for (int k=i;k<=i+x-1;++k)
					for (int l=j;l<=j+y-1;++l)
					{
					
						b[k][l]-=num;
						if (b[k][l]<0)
							return false;
					}
			}
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			if (b[i][j]>0)
				return false;
	return true;
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			scanf("%d",&a[i][j]),sum+=a[i][j];
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m;++j)
			if (check(i,j))
				ans=min(ans,sum/i/j);
	printf("%d",ans);
}

2241. [SDOI2011]打地鼠【暴力+剪枝】