Codeforces Round #677 (Div. 3) F (多维dp)

在这里插入图片描述
题意:给定你一个n*m的矩阵,然后每一行中可以取一个或者多个元素,(每行最多m/2个),然后问你这些数的和在能整除k的情况下的最大的可能值是多少?
数据量不大的情况下,看到最大的这种限制条件,很容易想到dp,但我又不会,剽窃他人智慧
因为这道题是在一个二维的矩阵中操作,并且还附加了一个特定的取模值的条件,所以我们开一个四维空间的dp数组,(2e7左右,极限) 。
dp[ i ][ j ][ l ][ r ] 代表到第i,j位置取了l个数的时候,这l个数的和对k取模为r的情况下,最大值为多少。
我们先考虑从j-1状态推得j,我们要找到的一个j-1状态下的模x使得(x + a[i][j]%k)%k == r,r也就是第j个位置当前的余数,所以这样推我们还要枚举找到x。
然后我们就考虑用j去推j+1,会有两种情况:
1.我们不取第j+1位置上的这个数,此时dp[ i ][ j + 1][ l ][ r ] = max(dp[ i ][ j + 1][ l ][ r ],dp[ i ][ j ][ l - 1][ r ]);
2.我们取第j+1个位置时,dp[ i ][ j + 1][ l ][ (r + mp[ i ][j + 1])%k ] = max(dp[ i ][ j + 1][ l ][ (r + mp[ i ][j + 1])%k ],dp[ i ][ j ][ l - 1][ r ] + mp[i][j+1];
还有就是我们也要考虑层之间的关系,因为当前这一层的状态是由上一层的状态退出的,所以第i+1层的初始状态还应该由第i层更新一下:
dp[i+1][0][0][r] = max(dp[i][j][l][r],dp[i+1][0][0][r]);
然后我们答案应该就是第n+1层的初始状态dp[n+1][0][0][0]。

AC代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 77;
const int INF = 0x3f3f3f3f;

int mp[MAXN][MAXN];
int dp[MAXN][MAXN][MAXN][MAXN];
//dp[i][j][l][r]第i行第j位选择了l个数中且这l个数对k的余数为r的和的最大值为多少
//每一行中最多选择 m/2 个元素
int n,m,k;

int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i = 1;i <= n;i ++){
		for(int j = 1;j <= m;j ++){
			scanf("%d",&mp[i][j]);
		}
	}
	for(int i = 0;i <= n;i ++)
		for(int j = 0;j <= m;j ++)
			for(int l = 0;l <= m;l ++)//选几个数
				for(int r = 0;r < k;r ++)
					dp[i][j][l][r] = -INF;
	//memset(dp,-INF,sizeof(dp));
	dp[1][0][0][0] = 0;
	for(int i = 1;i <= n;i ++){
		for(int j = 0;j < m;j ++){//这里是递推的j+1 从0开始 到m-1结束
			for(int l = 0;l <= min(j+1,m/2);l ++){
				for(int r = 0;r < k;r ++){
					dp[i][j+1][l][r] = max(dp[i][j+1][l][r],dp[i][j][l][r]);//不选择第j+1个数 直接转移过来
					if(l && dp[i][j][l-1][r] != -INF)//如果长度不为0并且前面的数选过了 就选择第j个数
						dp[i][j+1][l][(r + mp[i][j+1])%k] = max(dp[i][j+1][l][(r + mp[i][j+1])%k],dp[i][j][l-1][r] + mp[i][j+1]);
				}
				for(int r = 0;r < k;r ++)
					dp[i+1][0][0][r] = max(dp[i+1][0][0][r],dp[i][j+1][l][r]);//更新当前位置的时候 同时实时更新到下一行
			}
		}
	}
	printf("%d\n",dp[n+1][0][0][0]);//第n+1行代表的就是 第n行统计过的答案
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值