CodeForces - 958C2 Encryption (medium)(区间dp)

题目链接:点击这里

题目大意:
给定一串长度为 n n n 的数字,将其分割为 k k k 段区间。各段区间数字相加模 p p p 后求和,求其最大值

题目分析:
容易想到一种 O ( n 2 k ) O(n^2k) O(n2k) 的区间 d p dp dp 做法:定义状态 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示用前 i i i 个数分了 k k k 段的最大值
转移为 d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ o ] [ j − 1 ] + ( s u m [ i ] − s u m [ o ] ) % p ) dp[i][j]=max(dp[i][j],dp[o][j-1]+(sum[i]-sum[o])\%p) dp[i][j]=max(dp[i][j],dp[o][j1]+(sum[i]sum[o])%p)

考虑优化,发现 p ≤ 100 p\le 100 p100 ,范围很小,所以尝试从此处入手, 枚举模 p p p 后的余数
状态 d p [ s u m [ i ] % p ] [ j ] dp[sum[i]\%p][j] dp[sum[i]%p][j]含义变为了前 i i i 个数模 p p p 结果为 s u m [ i ] % p sum[i]\%p sum[i]%p 分为了 j j j 组的最大值,最后答案就是 d p [ s u m [ n ] % p [ j ] dp[sum[n]\%p[j] dp[sum[n]%p[j]
其转移方程为 d p [ s u m [ i ] ] [ j ] = m a x ( d p [ s u m [ i ] [ j ] , d p [ o ] [ j − 1 ] + ( s u m [ i ] % p − o ) % p ) dp[sum[i]][j] = max(dp[sum[i][j],dp[o][j-1]+(sum[i]\%p-o)\%p) dp[sum[i]][j]=max(dp[sum[i][j],dp[o][j1]+(sum[i]%po)%p)
这样时间复杂度就变成了 O ( n k p ) O(nkp) O(nkp)

具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
#define int ll
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 1e5+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,k,p,a[maxn],sum[maxn],dp[maxn][55];
signed main()
{
	n = read(),k = read(),p = read();
	for(int i = 1;i <= n;i++) a[i] = read()%p,sum[i] = (sum[i-1]+a[i])%p;
	memset(dp,-127,sizeof(dp));
	dp[0][0] = 0;
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= k;j++)
			for(int o = 0;o < p;o++)
				dp[sum[i]][j] = max(dp[sum[i]][j],dp[o][j-1]+(sum[i]-o+p)%p);
	cout<<dp[sum[n]][k]<<endl;
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值