洛谷 P2484 [SDOI2011]打地鼠

洛谷 P2484 [SDOI2011]打地鼠


題目

題目描述

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

遊戲中的錘子每次只能打一隻地鼠,如果多隻地鼠同時探出頭,玩家只能通過多次揮舞錘子的方式打掉所有的地鼠。你認爲這錘子太沒用了,所以你改裝了錘子,增加了錘子與地面的接觸面積,使其每次可以擊打一片區域。如果我們把地面看做m*n的方陣,其每個元素都代表一個地鼠洞,那麼錘子可以覆蓋R*C區域內的所有地鼠洞。但是改裝後的錘子有一個缺點:每次揮舞錘子時,對於這 的區域中的所有地洞,錘子會打掉恰好一隻地鼠。也就是說錘子覆蓋的區域中,每個地洞必須至少有1只地鼠,且如果某個地洞中地鼠的個數大於1,那麼這個地洞只會有1只地鼠被打掉,因此每次揮舞錘子時,恰好有R*C只地鼠被打掉。由於錘子的內部結構過於精密,因此在遊戲過程中你不能旋轉錘子(即不能互換R和C)。

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

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

輸入輸出格式

輸入格式:
第一行包含兩個正整數m和n;

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

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

輸入輸出樣例

輸入樣例#1:

3 3
1 2 1
2 4 2
1 2 1

輸出樣例#1:

4

說明

【樣例說明】

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

【數據規模和約定】

對於30%的數據,m,n<=5 ;

對於60%的數據,m,n<=30 ;

對於100%的數據,1<=m,n<=100 ,其他數據不小於0,不大於10^5 。


題解

暴力+剪枝+容斥

首先,因爲錘子的大小不能改變且不能旋轉,所以圖上地鼠的總數必定是錘子大小的倍數(剪枝1)

然後,假如我們先確定了一個可行的解,那麼需要錘的次數大於該解的一定可以跳過(剪枝2)

最後就是判斷的方法

f [i ][j ]表示以從(1,1)到(ij )這個矩陣中所有點爲左上角所需要敲的總次數

那麼,很顯然以(ij )爲左上角所需要敲得次數就是map [i ][j ]-f [i -1][j ]+f [i ][j -1]-f [i -1][j -1]-f [i -x ][j ]-f [i ][j -y ]+f [i -x ][j -y ]次,如果所需要敲的次數小於0,那麼這個方案不是正確的方案


代碼

#include<cstdio>
#include<cstring>
using namespace std;

int n,m,sum,ans;
int map[105][105],f[105][105];

int readln()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x;
}

int max(int x,int y){return x>y?x:y;}

bool check(int x,int y)
{
    memset(f,sizeof(f),0);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1];
            int ret=f[i][j]-f[max(i-x,0)][j]-f[i][max(j-y,0)]+f[max(i-x,0)][max(j-y,0)];
            if (ret>map[i][j]) return false;
            f[i][j]+=map[i][j]-ret;
        }
    return true;
}

int main()
{
    n=readln();m=readln();
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            map[i][j]=readln();
            sum+=map[i][j];
        }
    ans=sum;
    for (int r=1;r<=n;r++)
        for (int c=1;c<=m;c++)
        {
            if (sum%(r*c)!=0||sum/(r*c)>=ans) continue;
            if (check(r,c)) ans=sum/(r*c);
        }
    printf("%d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章