蓝桥杯:试题 算法训练 采油区域 矩阵前缀和+动态规划+分治+枚举

  • Post author:
  • Post category:其他



资源限制


时间限制:2.0s 内存限制:512.0MB


采油区域


Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井。被拍卖的整块土地为一个矩形区域,被划分为M×N个小块。

Siruseri地质调查局有关于Navalur土地石油储量的估测数据。这些数据表示为M×N个非负整数,即对每一小块土地石油储量的估计值。

为了避免出现垄断,政府规定每一个承包商只能承包一个由K×K块相连的土地构成的正方形区域。

AoE石油联合公司由三个承包商组成,他们想选择三块互不相交的K×K的区域使得总的收益最大。

例如,假设石油储量的估计值如下:

1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 8 8 8 8 8 1 1 1
1 8 8 8 8 8 1 1 1
1 8 8 8 8 8 1 1 1
1 1 1 1 8 8 8 1 1
1 1 1 1 1 1 8 8 8
1 1 1 1 1 1 9 9 9
1 1 1 1 1 1 9 9 9

如果K = 2, AoE公司可以承包的区域的石油储量总和为100, 如果K = 3, AoE公司可以承包的区域的石油储量总和为208。

AoE公司雇佣你来写一个程序,帮助计算出他们可以承包的区域的石油储量之和的最大值。


输入格式


输入第一行包含三个整数M, N, K,其中M和N是矩形区域的行数和列数,K是每一个承包商承包的正方形的大小(边长的块数)。接下来M行,每行有N个非负整数表示这一行每一小块土地的石油储量的估计值。


输出格式


输出只包含一个整数,表示AoE公司可以承包的区域的石油储量之和的最大值。


数据规模和约定


数据保证K≤M且K≤N并且至少有三个K×K的互不相交的正方形区域。其中30%的输入数据,M, N≤ 12。所有的输入数据, M, N≤ 1500。每一小块土地的石油储量的估计值是非负整数且≤ 500。


样例输入

9 9 3
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 8 8 8 8 8 1 1 1
1 8 8 8 8 8 1 1 1
1 8 8 8 8 8 1 1 1
1 1 1 1 8 8 8 1 1
1 1 1 1 1 1 8 8 8
1 1 1 1 1 1 9 9 9
1 1 1 1 1 1 9 9 9


样例输出

208



思路

很麻烦的一个题,首先加和的话肯定先算前缀和,常数时间查询一个子矩阵的加和



关于矩阵前缀和

然后划分出3个不相交的子矩阵,一共有6中划分方式,这里体现了

分治思想


在这里插入图片描述

那么如何算每一块的最大值呢?这里要用到动态规划了,而且动态规划要求解四次,分别对应四个问题:

  • 矩阵【左上角到(x, y)】的子矩阵中最大加和的

    k*k

    的矩阵
  • 矩阵【右上角到(x, y)】的子矩阵中最大加和的

    k*k

    的矩阵
  • 矩阵【左下角到(x, y)】的子矩阵中最大加和的

    k*k

    的矩阵
  • 矩阵【右下角到(x, y)】的子矩阵中最大加和的

    k*k

    的矩阵



关于这个应用的动态规划求解

用四个dp数组,分别为

ul, ur, dl, dr

,表示上述四个动态规划问题的解数组

上述方法求得四个dp矩阵,那么可以开始暴力穷举每一种分法的边界,这里体现了

枚举思想

对于1-4种分法,穷举点,比如

分法3

,点将矩阵分为三个区域:左上,左下,右边

对于5-6种分法,穷举中间的两个横杠,横杠的间距为k,那么问题变为:上面,中间,下面

上下的最值好求,中间的最值需要枚举另一坐标轴,滑动一个k*k的矩阵,这矩阵夹在两横杠中间滑动,寻找最大值



代码

#include <bits/stdc++.h>

using namespace std;

int a[1509][1509], ul[1509][1509], ur[1509][1509], dl[1509][1509], dr[1509][1509];
int m, n, k;

int kksum(int x1, int y1, int x2, int y2)
{
	return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
}

int main()
{	
	cin>>m>>n>>k;
	memset(a, 0, sizeof(a));
	memset(ul, 0, sizeof(ul));
	memset(ur, 0, sizeof(ur));
	memset(dl, 0, sizeof(dl));
	memset(dr, 0, sizeof(dr));
	
	// 计算行前缀和 
	for(int i=1; i<=m; i++)
		for(int j=1; j<=n; j++) 
			cin>>a[i][j], a[i][j]+=a[i][j-1]; 
	// 计算列前缀和 
	for(int i=1; i<=m; i++)
		for(int j=1; j<=n; j++)
			a[i][j] += a[i-1][j];
	// ul
	for(int i=k; i<=m; i++)
		for(int j=k; j<=n; j++)
			ul[i][j] = max(kksum(i-k+1, j-k+1, i, j), max(ul[i-1][j], ul[i][j-1]));
	// ur	
	for(int i=k; i<=m; i++)
		for(int j=n-k+1; j>=1; j--)
			ur[i][j] = max(kksum(i-k+1, j, i, j+k-1), max(ur[i-1][j], ur[i][j+1]));
	// dl		
	for(int i=m-k+1; i>=1; i--)
		for(int j=k; j<=n; j++)
			dl[i][j] = max(kksum(i, j-k+1, i+k-1, j), max(dl[i+1][j], dl[i][j-1]));
	// dr	
	for(int i=m-k+1; i>=1; i--)
		for(int j=n-k+1; j>=1; j--) 
			dr[i][j] = max(kksum(i, j, i+k-1, j+k-1), max(dr[i+1][j], dr[i][j+1]));
	
	int ans = 0;
	// 枚举点,对应分法1-4 
	for(int i=1; i+1<=m; i++)
	{
		for(int j=1; j+1<=n; j++)
		{
			ans = max(ans, ul[i][n]+dl[i+1][j]+dr[i+1][j+1]);
			ans = max(ans, dr[i+1][1]+ul[i][j]+ur[i][j+1]);
			ans = max(ans, ur[m][j+1]+ul[i][j]+dl[i+1][j]);
			ans = max(ans, dl[1][j]+ur[i][j+1]+dr[i+1][j+1]);
		}
	}
	// 枚举中间的横杠,对应分法5-6 
	for(int i=k; i+k+1<=m; i++)
	{
		int up = ul[i][n];
		int dw = dl[i+k+1][n];
		int mid = 0;
		for(int j=k; j<=n; j++)
			mid = max(mid, kksum(i+1, j-k+1, i+k, j));
		ans = max(ans, up+dw+mid);
	}		
	for(int j=k; j+k+1<=n; j++)
	{
		int le = max(ul[k][j], dl[k+1][j]);
		int ri = max(ur[k][j+k+1], dr[k+1][j+k+1]);
		int mid = 0;
		for(int i=k; i<=m; i++)
			mid = max(mid, kksum(i-k+1, j+1, i, j+k));
		ans = max(ans, le+ri+mid);
	}
	
	cout<<ans<<endl;
	
	return 0;
}



版权声明:本文为weixin_44176696原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。