CJOJ 2421 黄金矿工

黄金矿工

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

Tag

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

Solution

这道题要用子矩阵DP,其实我也不知道自己用的什么(感觉就是写了个前缀和加优化)……

输入的时候先记录一个前缀和s[i][j](表示第i行从第1列到第j列的和),s[i][j] = s[i][j - 1] + a[i][j]

求最值的时候不用一个一个的枚举两边界点,直接枚举两边界点的列,然后在这两列间一行一行的计算,每加一行的值,就更新一次答案,如果加入这一行使该两列间的值小于0,就清空已选行,从改行下一行开始选,最后输出最大值减去10后的值即可

Code

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define L 310
#define LL long long
#define inf 9000100
using namespace std;

int n, a[L][L];
LL s[L][L], ans;

int main() {
  freopen("miner.in", "r", stdin);
  freopen("miner.out", "w", stdout);
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j) {
      scanf("%d", &a[i][j]);
      if (a[i][j] == 0) a[i][j] -= inf;
      s[i][j] = s[i][j - 1] + a[i][j];
    }
  for (int i = 1; i <= n; ++i)
    for (int j = i; j <= n; ++j) {
      LL cnt = 0;
      for (int k = 1; k <= n; ++k) {
	cnt += s[k][j] - s[k][i - 1];
	ans = max(cnt, ans);
	if (cnt < 0) cnt = 0;
      }
    }
  ans -= 10;
  if (ans > 0) printf("%lld\n", ans);
  else printf("0\n");
  return 0;
}

Summary

考试的时候只是想到了前缀和,然后一个一个边界点的枚,于是O(n^4)的T飞~~~

你可能感兴趣的:(考试,dp)