• [Tyvj Aug11] 黄金矿工


    传送门

    Description

    黄金矿工是一个经典的小游戏,它可以锻炼人的反应能力。该游戏中,可以通过“挖矿”获得积分并不断升级。玩家可以在线玩flash版黄金矿工,也可以下载后玩单机版黄金矿工。目前,黄金矿工小游戏有多个版本,例如黄金矿工双人版,黄金矿工单人版等。

    Jimmy是一位黄金矿工,他所在的金矿是一个n*n的矩形区域(俯视),区域内有黄金、石头和TNT,由一个 n*n的矩阵描述。黄金的价值对应矩阵中的正值,石头的价值对应矩阵中的负值,TNT由0表示。换句话说,挖到黄金赚钱,石头亏损,如果挖到TNT就挂了。

    Jimmy租到的挖矿工具很特别,它的形状是一个长宽任意(均为正整数)的矩形,可以取走被该工具覆盖的矩形区域内的所有物品,但如果该区域内有TNT,该工具将被炸毁,此时Jimmy将不得不赔偿矿主+∞元!!!需要注意的是,该工具只能在金矿范围内使用(即不得超出金矿边界),且租金为每次使用十元。

    现在,Jimmy想知道,如果他至多只有一次租用该工具的机会,他能获得的最大收益是多少。当然,如果Jimmy租用该工具无论如何都会亏损,他可以不租用,此时收益为0.

    Input

    第一行:一个整数n
    接下来n行,每行n个整数(绝对值<100),为题目中所描述的矩阵。

    Output

    一个数,即Jimmy所能获得的最大收益。

    Sample Input

    3
    0 -1 -1
    0 -12 0
    -19 0 0

    Sample Output

    0

    Hint

    【样例解释】
    无论Jimmy怎么挖矿,挖到的不是石头,就是TNT,总之无论如何都会亏损,所以选择不租用工具,收益为0

    【数据范围】
    对于30%的数据:0<n<=10
    对于60%的数据:0<n<=100
    对于100%的数据:0<n<=300

    Source

    动态规划, 贪心,子矩阵DP

     

     

    这是一道很好的DP题,首先这道题的模型就是求最大子矩阵和。很容易想到的暴力枚举是O(n^4)的,显然会TLE,我们发现,暴力枚举的时候我们很多东西都重复计算了,没有很好的利用中间结果。

    首先从一维的子序列最大和讲起。对于任意数列,O(n)的求它最大的子序列和,设f[i]表示到i位置的最大和,那么状态的转移只有两种,如果f[i-1]>0,那么 f[i]=f[i-1]+a[i],否则f[i]=a[i]。因为要求最大的子序列和,所以所选的子序列的起始位置一定是正数。

    当f[i]>0时,这个转移显然是对的,(每次转移后都会更新答案,所以a[i]<0也不会影响答案)

    当f[i-1]<0时,f[i-1]+a[i]一定小于 a[i],而i-1是从上一个起始位置开始,一段子序列中和第一次小于0的位置,同时序列的起点又是正数,所以上一次选的序列中不存在一个位置到当前位置的序列和大于0,所以新序列从 i 位置开始。

    然后类比到二维的子矩阵和,我们可以把一个矩形一列的和看做一维数组的一个元素,转化为上面那个问题,这样就只需枚举矩形的上下界,求和的话可以用前缀和优化,记录每一列的前缀和,这样就可以O(1)求矩形一列的和。总复杂度为O(n^3)。

    还可以参见这篇博客,博主讲得很清楚。

     

     1 #include <map>
     2 #include <set>
     3 #include <cmath>
     4 #include <ctime>
     5 #include <queue>
     6 #include <stack>
     7 #include <cstdio>
     8 #include <string>
     9 #include <vector>
    10 #include <cstdlib>
    11 #include <cstring>
    12 #include <iostream>
    13 #include <algorithm>
    14 using namespace std;
    15 #define ll long long
    16 
    17 inline int gi()
    18 {
    19     bool b=0; int r=0; char c=getchar();
    20     while(c<'0' || c>'9') { if(c=='-') b=!b; c=getchar(); }
    21     while(c>='0' && c<='9') { r=r*10+c-'0'; c=getchar(); }
    22     if(b) return -r; return r;
    23 }
    24 
    25 const int inf = 21400000, N = 307;
    26 int a[N][N];
    27 
    28 int main()
    29 {
    30     int i,j,k,sum,ans,n;
    31     n=gi();
    32     for (i=1; i<=n; i++)
    33         for (j=1; j<=n; j++)
    34             {
    35                 a[i][j]=gi();
    36                 if (!a[i][j]) a[i][j]=-inf;
    37                 a[i][j]+=a[i-1][j];  //记录每一列的前缀和
    38             }
    39     for (i=1; i<=n; i++)  //枚举子矩形下面边的位置
    40         for (k=0; k<i; k++)  //枚举子矩形上面边的位置
    41             {
    42                 sum=0;
    43                 for (j=1; j<=n; j++)  //枚举矩形的宽 若 sum 大于 0 则继续扩展 每次更新 ans 否则 sum=0 重新枚举一个子矩形 妙不可言
    44                     {
    45                         sum+=a[i][j]-a[k][j];
    46                         if (sum < 0) sum=0;
    47                         else ans=max(ans,sum);
    48                     }
    49             }
    50     printf("%d
    ",max(ans-10,0));
    51     return 0;
    52 }
  • 相关阅读:
    Hibernate---对象的三种状态
    grunt+bower依赖管理
    grunt 的安装和简单使用
    sqlserver dmv 动态管理视图
    ado.net 数据库连接池
    桥接模式
    .net MVP
    主定理(分治算法)
    图中环的判断
    选举协议paxos 协议 理解
  • 原文地址:https://www.cnblogs.com/y142857/p/7152992.html
Copyright © 2020-2023  润新知