• bzoj 2241: [SDOI2011]打地鼠


    2241: [SDOI2011]打地鼠

    2017-08-26


    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

    【样例说明】
    使用2*2的锤子,分别在左上、左下、右上、右下挥舞一次。
    【数据规模和约定】
    对于100%的数据,1<=M,N<=100,其他数据不小于0,不大于10^5

    暴力模拟+剪枝
    从大到小开始模拟锤子的大小,然后判断是否可以用这么大的锤子;
    优化1:如果锤子面积不能被地鼠总数整除,这个锤子就一定不是合法锤子;
    优化2:如果当前答案比(地鼠总数/锤子面积)小当前一定不是最优答案,可以删去;
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
    int m,n,sum;
    int a[109][109];
    int ans;int lin[109][109];
    bool pd(int c,int k){
        int re=0;
        for(int e=1;e<=n;e++)
            for(int r=1;r<=m;r++)lin[e][r]=a[e][r];
        for(int e=1;e+c-1<=n;e++)
            for(int r=1;r+k-1<=m;r++)
                for(int q=e+c-1;q>=e;q--)
                    for(int w=r+k-1;w>=r;w--)
            {    
                re+=lin[e][r];
                lin[q][w]-=lin[e][r];
                if(lin[q][w]<0)return 0;
            }
        if(sum==re)return 1;
        return 0;
    }
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){cin>>a[i][j];sum+=a[i][j];}
        ans=sum;
        for(int i=n;i>=1;i--)
            for(int j=m;j>=1;j--)
        {    
            if(sum==(sum/i/j)*i*j)
            if(ans>(sum/i/j))
            if(pd(i,j))   ans=min(sum/i/j,ans);
        }
        cout<<ans;
        return 0;
    }
    2241_cpp

    by:s_a_b_e_r


    暴力啊
    当然暴力啊
    暴力出奇迹啊
    n^2枚举锤子大小
    n^2枚举锤子砸的左上角
    n^2砸掉范围内的地鼠
    因为目标是把所有地鼠砸掉所以每锤砸掉的个数=左上角个数
    砸着砸着发现哪个格的地鼠数变负数了
    说明这个锤子不合法
    回去枚举下一个锤子
    共计n^6
    实力剪枝卡过去……
    具体优化楼上已经说了我就不说了(懒
    嗯你说正解是差分?
    ……
    暴力大法好(逃
    附代码
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=105;
    int m,n,sum;
    int a[N][N],b[N][N];
    int check(int r,int c)
    {
        int res=0;
        for(int i=1;i<=m-r+1;++i)
        for(int j=1;j<=n-c+1;++j)
        {
          if(b[i][j]<0)return sum;
          res+=b[i][j];
          for(int x=0;x<r;++x)
          for(int y=0;y<c;++y)
            if(x||y)b[i+x][j+y]-=b[i][j];
          b[i][j]=0;
        }
        for(int i=1;i<=m;++i)
        for(int j=1;j<=n;++j)
        if(b[i][j])return sum;
        return res;
    }
    int main()
    {
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;++i)
        for(int j=1;j<=n;++j)
        {scanf("%d",&a[i][j]);sum+=a[i][j];}
        int ans=sum;
        for(int r=1;r<=m;++r)
        for(int c=1;c<=n;++c)
        if(sum%(r*c)==0&&ans>sum/(r*c))
        {
          for(int i=1;i<=m;++i)
          for(int j=1;j<=n;++j)
            b[i][j]=a[i][j];
          ans=min(ans,check(r,c));
        }
        cout<<ans<<endl;
        return 0;
    }
    2241(wypx)

    s:哇,w抽出sr了,不行我还是去玩神代吧.....

    w:小姐姐真好看……不对快去做题啊>_<

  • 相关阅读:
    Hive中将文件加载到数据库表失败解决办法
    Hive安装及配置
    Hadoop下MapReduce实现Pi值的计算
    CentOS下Hadoop运行环境搭建
    kettle案例实现
    假期周总结报告03
    假期周总结报告02
    假期周进度报告01
    阅读笔记6
    阅读笔记5
  • 原文地址:https://www.cnblogs.com/ck666/p/7433799.html
Copyright © 2020-2023  润新知