P2331 [SCOI2005]最大子矩阵(dp)

在这里插入图片描述
思路:唉,dp是真的菜,想了半天屁思路都没。
先考虑一维情况,是求k个不相交的子段,区间和最大
状态为 d p ( i , j ) dp(i,j) dp(i,j)考虑前 i i i个数字选 j j j个区间.
如果考虑选用 a i a_i ai,那么有
d p ( i , j ) = m a x ( d p ( k , j − 1 ) + s u m [ i ] − s u m [ k − 1 ] ) dp(i,j)=max(dp(k,j-1)+sum[i]-sum[k-1]) dp(i,j)=max(dp(k,j1)+sum[i]sum[k1])
如果不选 a i a_i ai
d p ( i , j ) = d p ( i − 1 , j ) dp(i,j)=dp(i-1,j) dp(i,j)=dp(i1,j)
二维的情况的话,就是新增加一个维度,同时考虑两列的选取情况
d p ( i , j , k ) dp(i,j,k) dp(i,j,k):第一个列选择了 i i i个,第二列选择了 j j j个,选出了 k k k个子矩阵的情况
情况1:选了第一列的第 i i i个,但是没有选第二列的 j j j
d p ( i , j , k ) = m a x ( d p ( l , j − 1 , k − 1 ) + s u m [ i ] [ 1 ] − s u m [ l − 1 ] [ 1 ] ) dp(i,j,k)=max(dp(l,j-1,k-1)+sum[i][1]-sum[l-1][1]) dp(i,j,k)=max(dp(l,j1,k1)+sum[i][1]sum[l1][1])
情况2:只选了第二列,但没有选第一列
d p ( i , j , k ) = m a x ( d p ( i , l , k − 1 ) + s u m [ j ] [ 2 ] − s u m [ l − 1 ] [ 2 ] ) dp(i,j,k)=max(dp(i,l,k-1)+sum[j][2]-sum[l-1][2]) dp(i,j,k)=max(dp(i,l,k1)+sum[j][2]sum[l1][2])
情况3:同时选中 i , j i,j i,j两个数字, 要求 i = = j 要求i==j 要求i==j
d p ( i , j , k ) = m a x ( d p ( i − l , j − l , k − 1 ) + s u m [ i ] [ 1 ] − s u m [ i − l − 1 ] [ 1 ] + s u m [ j ] [ 2 ] − s u m [ j − l − 1 ] [ 2 ] dp(i,j,k)=max(dp(i-l,j-l,k-1)+sum[i][1]-sum[i-l-1][1]+sum[j][2]-sum[j-l-1][2] dp(i,j,k)=max(dp(il,jl,k1)+sum[i][1]sum[il1][1]+sum[j][2]sum[jl1][2]
情况4:两个都不选
d p ( i , j , k ) = m a x ( d p ( i − 1 , j − 1 , k ) ) dp(i,j,k)=max(dp(i-1,j-1,k)) dp(i,j,k)=max(dp(i1,j1,k))
然后笔者本人叭叭写了一堆,发现情况4想假了,事实上是两个不选/ i i i不选, j j j也不选,但是已经生成了 k k k块子矩阵
也就是:
情况4:
d p ( i , j , k ) = m a x ( d p ( i − 1 , j − 1 , k ) , d p ( i − 1 , j , k ) , d p ( i , j − 1 , k ) ) dp(i,j,k)=max(dp(i-1,j-1,k),dp(i-1,j,k),dp(i,j-1,k)) dp(i,j,k)=max(dp(i1,j1,k),dp(i1,j,k),dp(i,j1,k))
然后这题还可以选空矩阵,很坑,小于等于k的答案也是合法的.

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) (a).begin(), (a).end()
#define pb(a) push_back(a)
int a[maxn][3];
ll dp[maxn][maxn][12];
int sum[maxn][3];
int n,m,K;
void solve2(){
	for(int i=1;i<=n;i++){
		cin>>a[i][1]>>a[i][2];
	}
	for(int i=1;i<=n;i++){
		sum[i][1] = sum[i-1][1] + a[i][1];
		sum[i][2] = sum[i-1][2] + a[i][2];
	}
	for(int i=0;i<=n;i++){
		for(int j=0;j<=n;j++) dp[i][j][0] = 0;
	}
	ll ans = 0;
	for(int k=1;k<=K;k++){
		for(int i=0;i<=n;i++){
			for(int j=0;j<=n;j++){
				//情况4, i,j两个点都不选/其中有一个不选
				if(i>=1&&j>=1) dp[i][j][k] = max(dp[i][j][k],dp[i-1][j-1][k]);
				if(i>=1) dp[i][j][k] = max(dp[i][j][k],dp[i-1][j][k]);
				if(j>=1) dp[i][j][k] = max(dp[i][j][k],dp[i][j-1][k]);
				for(int l=1;l<=n;l++){
					//情况1,选择第一列中的[i-l,i]
					if(i>=l) dp[i][j][k] = max(dp[i][j][k],dp[i-l][j][k-1]+sum[i][1]-sum[i-l][1]);
					//情况2选择第二列中的[j-l,j]
					if(j>=l) dp[i][j][k] = max(dp[i][j][k],dp[i][j-l][k-1]+sum[j][2]-sum[j-l][2]);
					//情况2:同时选择i,j这两个格子,需要i==j这个条件
					if(i>=l&&j>=l&&i==j) dp[i][j][k] = max(dp[i][j][k],dp[i-l][j-l][k-1]+
					sum[i][1]-sum[i-l][1] + sum[j][2]-sum[j-l][2]
					);
				}
				ans = max(ans,dp[i][j][k]);
			}
		}
	}
	cout<<ans<<"\n";
}
ll dp1[maxn][maxn];
void solve1(){
	for(int i=1;i<=n;i++) cin>>a[i][1];
	for(int i=1;i<=n;i++) sum[i][1] = sum[i-1][1] + a[i][1];
//	for(int i=0;i<=n;i++){
//		for(int j=0;j<=K;j++) dp1[i][j] = -INF;
//	}
	ll ans = 0;
	for(int i=0;i<=n;i++) dp1[i][0] = 0;
	for(int j=1;j<=K;j++){
		for(int i=1;i<=n;i++){
			dp1[i][j] = max(dp1[i][j],dp1[i-1][j]);
			for(int l=0;l<=i;l++){
				dp1[i][j] = max(dp1[i][j],dp1[i-l][j-1]+sum[i][1]-sum[i-l][1]);
			}
			ans = max(ans,dp1[i][j]);
		}
	}
	cout<<ans<<"\n";
}
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m>>K;
//	for(int i=0;i<=n;i++){
//		for(int i1=0;i1<=n;i1++){
//			for(int j=0;j<=K;j++) dp[i][i1][j] = -INF;
//		}
//	}
	if(m==2) solve2();
	else solve1();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

minato_yukina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值