1. 程式人生 > 其它 >[APIO2009]採油區域(題解)

[APIO2009]採油區域(題解)

這是本蒟蒻的第一篇紫題題解

題目傳送門

這是一道比較考細節的題目

  • 經讀題可知要求一個矩陣三個邊長為 \(k\) 子矩陣的和的最大值值,因為要求子矩陣,所以我們可以採用二維字首和
  • 我們可以發現這個邊長為 \(k\) 的子矩陣分佈只會有 \(6\) 種情況(如下圖)(本人就是因為沒考慮到最後兩種而卡了半天)
  • 再靈活使用輔助陣列即可

參考程式碼

#include <bits/stdc++.h>
using namespace std;
int m, n, k;
int a[2005][2005], sum[2005][2005];
int a1[2005][2005], b1[2005][2005], c1[2005][2005], d1[2005][2005], e[2005][2005], f[2005][2005];
int Sum(int x1, int y1, int x2, int y2) {
	return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1]; 
}
int main() {
	cin >> n >> m >> k;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			cin >> a[i][j];
			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
		}
	}
	for (int i = k; i <= n; i++) {
		for (int j = k; j <= m; j++) {
			a1[i][j] = max(max(a1[i - 1][j], a1[i][j - 1]), Sum(i - k + 1, j - k + 1, i, j));
		}
	}
	//左上角最大
	for (int i = k; i <= n; i++) {
		for (int j = m - k + 1; j >= 1; j--) {
			b1[i][j] = max(max(b1[i - 1][j], b1[i][j + 1]), Sum(i - k + 1, j, i, j + k - 1));
		}
	} 
	//右上角最大
	for (int i = n - k + 1; i >= 1; i--) {
		for (int j = k; j <= m; j++) {
			c1[i][j] = max(max(c1[i + 1][j], c1[i][j - 1]), Sum(i, j - k + 1, i + k - 1, j));
		}
	} 
	//左下角最大
	for (int i = n - k + 1; i >= 1; i--) {
		for (int j = m - k + 1; j >= 1; j--) {
			d1[i][j] = max(max(d1[i + 1][j], d1[i][j + 1]), Sum(i, j, i + k - 1, j + k - 1));
		}
	} 
	//右下角最大
	for (int i = 1; i <= n - k + 1; i++) {
		for (int j = 1; j <= m - k + 1; j++) {
			e[i][i + k - 1] = max(e[i][i + k - 1], Sum(i, j, i + k - 1, j + k - 1));
		}
	}
	for (int n1 = k + 1; n1 <= n; n1++) {
		for (int i = 1, j = i + n1 - 1; j <= n; i++, j++) {
			e[i][j] = max(e[i + 1][j], e[i][j - 1]);
		}
	}
	//第 i ~ j行最大
	for (int i = 1; i <= m - k + 1; i++) {
		for (int j = 1; j <= n - k + 1; j++) {
			f[i][i + k - 1] = max(f[i][i + k - 1], Sum(j, i, j + k - 1, i + k - 1)); 
			
		}
	}
	for (int n1 = k + 1; n1 <= m; n1++) {
		for (int i = 1, j = i + n1 - 1; j <= m; i++, j++) {
			f[i][j] = max(f[i + 1][j], f[i][j - 1]);
		}
	}
	//第 i ~ j列最大
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			ans = max(ans, e[i + 1][n] + a1[i][j] + b1[i][j + 1]);
			ans = max(ans, e[1][i] + c1[i + 1][j] + d1[i + 1][j + 1]);
			ans = max(ans, f[j + 1][m] + a1[i][j] + c1[i + 1][j]);
			ans = max(ans, f[1][j] + b1[i][j + 1] + d1[i + 1][j + 1]);
		}
	} 
	//一橫一豎四種情況 
	for (int i = 1; i <= n; i++) {
		for (int j = i + 1; j <= n; j++) {
			ans = max(ans, e[1][i] + e[i + 1][j] + e[j + 1][n]);
		}
	}
	//兩個橫著 
	for (int i = 1; i <= m; i++) {
		for (int j = i + 1; j <= m; j++) {
			ans = max(ans, f[1][i] + f[i + 1][j] + f[j + 1][m]);
		}
	}
	//兩個豎著
	cout << ans; 
	return 0;
}