[APIO2009] 采油区域:动态规划

本文介绍了一种利用动态规划解决石油资源拍卖问题的方法,旨在帮助Aoe石油联合公司在Navalur省土地拍卖中,找到三个互不相交的K×K区域,使总收益最大化。文章详细解释了算法步骤,包括前缀和计算、K×K区域价值求和及递推公式应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:
Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井。被拍卖的整块土地为一个矩形区域,被划分为M×N个小块。 Siruseri地质调查局有关于Navalur土地石油储量的估测数据。这些数据表示为M×N个正整数,即对每一小块土地石油储量的估计值。 为了避免出现垄断,政府规定每一个承包商只能承包一个由K×K块相连的土地构成的正方形区域。 AoE石油联合公司由三个承包商组成,他们想选择三块互不相交的K×K的区域使得总的收益最大。 例如,假设石油储量的估计值如下:
在这里插入图片描述
如果K = 2, AoE公司可以承包的区域的石油储量总和为100, 如果K = 3, AoE公司可以承包的区域的石油储量总和为208。 AoE公司雇佣你来写一个程序,帮助计算出他们可以承包的区域的石油储量之和的最大值。

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

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

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

样例输入:
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

解题思路:动态规划问题,使用递推,先将小问题的解求出来,然后再解决大问题。递推+前缀和
一个矩形,分三块,有六种方法 如图:
在这里插入图片描述在这里插入图片描述

分别在三个部分中找的k*k面积最大的,加起来就是答案
那么怎么表示这三个块中面积最大的呢?
就需要记录对于每个点
它左上,右上,左下,右下的四个部分中,最大的K * K块的价值和
这个样子:

int a[2000][2000], b[2000][2000], c[2000][2000], d[2000][2000];
//a表示左上,b表示右上,c表示左下,d表示右下

先将循环用define简化:

#define FI for(int i=1;i<=n;i++)
#define FJ for(int j=1;j<=m;j++)
#define FDI for(int i=n;i>=k;i--)
#define FDJ for(int j=m;j>=k;j--)
#define FUI for(int i=k;i<=n;i++)
#define FUJ for(int j=k;j<=m;j++)
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)

用数组mp先表示以i,j点为右下角的的矩形(注意不是k*k的矩形)的储油量之和,然后通过循环再计算用mp表示以i,j为右下角的k * k的矩形的储油量之和:

	FI FJ{
			cin>>temp;
			pos[i][j] = pos[i - 1][j] + pos[i][j - 1] - pos[i - 1][j - 1] + temp;
		}
	FDI FDJ pos[i][j] -= pos[i - k][j] + pos[i][j - k] - pos[i - k][j - k];

再通过递推公式计算出左上角,右上角,左下角,右下角的储油量最大值:

	FUI FUJ a[i][j] = max(pos[i][j], max(a[i - 1][j], a[i][j - 1]));
	FUI FDJ b[i][j] = max(pos[i][j], max(b[i - 1][j], b[i][j + 1]));
	FDI FUJ c[i][j] = max(pos[i][j], max(c[i + 1][j], c[i][j - 1]));
	FDI FDJ d[i][j] = max(pos[i][j], max(d[i + 1][j], d[i][j + 1]));

在根据上面分析的六种情况,算出最大的储油量res:

	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[i][j] + b[i][j + k] + c[i + k][m]);
	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[i][m] + c[i + k][j] + d[i + k][j + k]);
	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[i][j] + b[n][j + k] + c[i + k][j]);
	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[n][j] + b[i][j + k] + d[i + k][j + k]);
	rep(i, k, n - k) rep(j, k + k, m - k) res = max(res, a[n][j - k] + b[n][j + k] + pos[i][j]);
	rep(i, k + k, n - k) rep(j, k, m - k) res = max(res, a[i - k][m] + c[i + k][m] + pos[i][j]);

最终输出res:

cout << res;

完整代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

//定义循环符号
#define FI for(int i=1;i<=n;i++)
#define FJ for(int j=1;j<=m;j++)
#define FDI for(int i=n;i>=k;i--)
#define FDJ for(int j=m;j>=k;j--)
#define FUI for(int i=k;i<=n;i++)
#define FUJ for(int j=k;j<=m;j++)
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
using namespace std;

int a[2000][2000], b[2000][2000], c[2000][2000], d[2000][2000];
//a表示左上,b表示右上,c表示左下,d表示右下
int pos[2000][2000];
int n, m, k;

int main() {

	cin>>n>>m>>k;
	int temp;
	int res = 0;

	FI FJ{
		cin>>temp;
		pos[i][j] = pos[i - 1][j] + pos[i][j - 1] - pos[i - 1][j - 1] + temp;
	}
	FDI FDJ pos[i][j] -= pos[i - k][j] + pos[i][j - k] - pos[i - k][j - k];
	FUI FUJ a[i][j] = max(pos[i][j], max(a[i - 1][j], a[i][j - 1]));
	FUI FDJ b[i][j] = max(pos[i][j], max(b[i - 1][j], b[i][j + 1]));
	FDI FUJ c[i][j] = max(pos[i][j], max(c[i + 1][j], c[i][j - 1]));
	FDI FDJ d[i][j] = max(pos[i][j], max(d[i + 1][j], d[i][j + 1]));
	
	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[i][j] + b[i][j + k] + c[i + k][m]);
	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[i][m] + c[i + k][j] + d[i + k][j + k]);
	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[i][j] + b[n][j + k] + c[i + k][j]);
	rep(i, k, n - k) rep(j, k, m - k) res = max(res, a[n][j] + b[i][j + k] + d[i + k][j + k]);
	rep(i, k, n - k) rep(j, k + k, m - k) res = max(res, a[n][j - k] + b[n][j + k] + pos[i][j]);
	rep(i, k + k, n - k) rep(j, k, m - k) res = max(res, a[i - k][m] + c[i + k][m] + pos[i][j]);
	
	cout << res;
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值