【BZOJ1084】[SCOI2005]最大子矩阵【DP】

【题目链接】

终于A了这题了。

m = 1和m = 2两种情况分开做。


对于m = 1,很简单的DP。设dp[k][n]表示前n个数字,分为k段的最大权值。

(1)不取第n个数,dp[k][n] = dp[k][n - 1];

(2)取第n个数,并组成新的一段,dp[k][n] = max{dp[k - 1][j] + sum[n] - sum[j]} (0 <= j < i) 

注意j可以取到0。


对于m = 2,也是类似的。设dp[k][n][m]表示第一列到前n个数字,第二列到前m个数字,分为k个子矩阵的最大权值。

(1)不取第n个数或者不取第m个数,dp[k][n][m] = max{dp[k][n - 1][m], dp[k][n][m - 1]}

(2)第k个子矩阵是从第一列第n个数向上延伸的,dp[k][n][m] = max{dp[k - 1][i][m] + sum[n][0] - sum[i][0]} (0 <= i < n),其中sum[n][0]代表第一列的前缀和。

(3)第k个子矩阵是从第二列第m个数向上延伸的,dp[k][n][m] = max{dp[k - 1][n][j] + sum[m][1] - sum[j][1]} (0 <= j < m),其中sum[m][1]代表第二列的前缀和。

(4)当n == m时,第k个子矩阵从n和m同时向上延伸,dp[k][n][m] = max{dp[k - 1][i][i] + sum[n][0] - sum[i][0] + sum[m][1] - sum[i][1]} (0 <= i < n)。


完啦。

/* Pigonometry */
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 105, maxk = 12;

int n, m, k;

inline int iread() {
	int f = 1, x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return f * x;
}

inline void solve1() {
	static int sum[maxn], dp[maxk][maxn];
	memset(sum, 0, sizeof(sum)); memset(dp, 0, sizeof(dp));
	for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + iread();

	for(int t = 1; t <= k; t++) for(int i = 1; i <= n; i++) {
		dp[t][i] = dp[t][i - 1];
		for(int j = 0; j < i; j++)
			dp[t][i] = max(dp[t][i], dp[t - 1][j] + sum[i] - sum[j]);
	}

	printf("%d\n", dp[k][n]);
}

inline void solve2() {
	static int sum[maxn][2], dp[maxk][maxn][maxn];
	memset(sum, 0, sizeof(sum)); memset(dp, 0, sizeof(dp));
	for(int i = 1; i <= n; i++) {
		sum[i][0] = sum[i - 1][0] + iread();
		sum[i][1] = sum[i - 1][1] + iread();
	}

	for(int t = 1; t <= k; t++) for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) {
		dp[t][i][j] = max(dp[t][i - 1][j], dp[t][i][j - 1]);
		for(int l = 0; l < i; l++)
			dp[t][i][j] = max(dp[t][i][j], dp[t - 1][l][j] + sum[i][0] - sum[l][0]);
		for(int l = 0; l < j; l++)
			dp[t][i][j] = max(dp[t][i][j], dp[t - 1][i][l] + sum[j][1] - sum[l][1]);
		if(i == j) for(int l = 0; l < i; l++)
			dp[t][i][j] = max(dp[t][i][j], dp[t - 1][l][l] + sum[i][0] - sum[l][0] + sum[j][1] - sum[l][1]);
	}

	printf("%d\n", dp[k][n][n]);
}

int main() {
	n = iread(); m = iread(); k = iread();
	if(m == 1) solve1();
	else if(m == 2) solve2();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值