首页 > 技术文章 > Codeforces 1433F Zero Remainder Sum

StarRoadTang 2020-10-21 05:59 原文

题目链接

点我跳转

题目大意

给定一个 \(N × M\) 的矩阵

对于矩阵的每一行,你可以在该行挑选不超过 \(⌊m/2⌋\) 个元素

要求挑选的所有元素的和为 \(K\) 的倍数,问可以挑选的最大元素和为多少

解题思路

先考虑如果题目给的是个一维数组而不是二维矩阵该怎么做 \(?\)

可以定义 \(dp[N][N][N][2]\) ,其含义为 \(↓\)

当前在第 \(j\) 个位置,已经选择了 \(k\) 个数 ,挑选的元素的和模完 \(K\) 之后的值为 \(L\)

\(0\) 表示元素 \(a[j]\) 不挑选 , \(1\) 表示元素 \(a[j]\) 挑选

那么不难得到转移方程

\(dp[j][k][l][0] = max(dp[j - 1][k][l][0] , dp[j - 1][k][l][1])\)

\(dp[j][k][(l + x)\%K][1] = max(dp[j - 1][k - 1][l][0] , dp[j - 1][k - 1][l][1]) + a[j]\)

而把一维延伸至二维只要在以上操作上再做一次竖向的 \(dp\) 即可

AC_Code

#include<bits/stdc++.h>
using namespace std;
const int N = 80;
int a[N][N] , ans[N][N] , dp[N][N][N][2];
signed main()
{
	memset(ans , 128 , sizeof(ans)) , ans[0][0] = 0;
	int n , m , K , res = 0;
	cin >> n >> m >> K;
	for(int i = 1 ; i <= n ; i ++) for(int j = 1 ; j <= m ; j ++) cin >> a[i][j];
	for(int i = 1 ; i <= n ; i ++)
	{	
		memset(dp , 128 , sizeof(dp));
		for(int j = 0 ; j <= m ; j ++) dp[j][0][0][0] = 0;
		for(int j = 1 ; j <= m ; j ++)
		{
			int x = a[i][j];
			for(int l = 0 ; l < K ; l ++)
			{
				for(int k = 1 ; k <= m / 2 ; k ++)
				{
					int ha = max(dp[j - 1][k][l][0] , dp[j - 1][k][l][1]);
					dp[j][k][l][0] = max(dp[j][k][l][0] , ha);
					int he = -0x3f3f3f3f;
					if(k) he = max(dp[j - 1][k - 1][l][0] , dp[j - 1][k - 1][l][1]) + a[i][j];
					dp[j][k][(l + x) % K][1] = max(dp[j][k][(l + x) % K][1] , he);			
				}
			}	
		}
		for(int h = 0 ; h <= m / 2 ; h ++) for(int l = 0 ; l < K ; l ++) for(int L = 0 ; L < K ; L ++)
		{
			int x = max(max(dp[m][h][L][0] , dp[m][h][L][1]) , 0);
			int y = ans[i][(l + x) % K];
			ans[i][l] = max(ans[i][l] , ans[i - 1][l]);
			ans[i][(l + x) % K] = max(max(y , ans[i - 1][(l + x) % K]) , ans[i - 1][l] + x);
		}
	}	
	res = max(res , ans[n][0]);
	cout << res << '\n';
	return 0;
}

推荐阅读