codeforces 958C2 区间DP

codeforces 958C2


题意:

给 定 一 串 长 度 为 n 的 数 字 , 将 其 分 割 为 k 段 区 间 。 各 段 区 间 数 字 相 加 , 对 p 取 模 。 给定一串长度为n的数字,将其分割为k段区间。各段区间数字相加,对p取模。 nkp
问 k 段 区 间 最 大 和 。 问k段区间最大和。 k


题解:

d p [ i ] [ j ] 表 示 长 度 为 i 的 子 串 分 割 为 j 段 的 最 优 解 , 用 s u m [ i ] 维 护 区 间 [ 1 , i ] 的 前 缀 和 。 dp[i][j]表示长度为i的子串分割为j段的最优解,用sum[i]维护区间[1,i]的前缀和。 dp[i][j]ijsum[i][1,i]

  • d p [ i ] [ j ] = m a x ( d p [ i ] ] [ j ] , d p [ i ] [ j − 1 ] + ( s u m [ r ] − s u m [ l ] + p ) % p ) dp[i][j] = max(dp[i]][j], dp[i][j-1]+(sum[r]-sum[l]+p)\%p) dp[i][j]=max(dp[i]][j],dp[i][j1]+(sum[r]sum[l]+p)%p)
  • a n s = d p [ n ] [ k ] ans=dp[n][k] ans=dp[n][k]
时间复杂度
  • O ( n 2 k ) O(n^2k) O(n2k)

区间DP优化

由 于 n ≤ 20000 , 显 然 超 时 。 而 f [ i ] &lt; p ≤ 100 , 故 将 枚 举 1 — l 改 为 枚 举 0 — p 。 由于n≤20000,显然超时。而f[i]&lt;p≤100,故将枚举1—l改为枚举0—p。 n20000f[i]<p1001l0p
d p [ f [ i ] ] [ j ] 表 示 值 为 f [ i ] 的 子 串 分 割 为 j 段 的 最 优 解 。 dp[f[i]][j]表示值为f[i]的子串分割为j段的最优解。 dp[f[i]][j]f[i]j

  • d p [ f [ i ] ] [ j ] = m a x ( d p [ f [ i ] ] [ j ] , d p [ m o d ] [ j − 1 ] + ( s u m [ i ] − m o d + p ) % p ) dp[f[i]][j] = max(dp[f[i]][j], dp[mod][j-1]+(sum[i]-mod+p)\%p) dp[f[i]][j]=max(dp[f[i]][j],dp[mod][j1]+(sum[i]mod+p)%p)
  • a n s = d p [ s u m [ n ] ] [ k ] ans=dp[sum[n]][k] ans=dp[sum[n]][k]
时间复杂度
  • O ( p 2 k ) O(p^2k) O(p2k)

#include <bits\stdc++.h>
using namespace std;
const int N = 20001;
long long sum[N];
long long dp[N][51];

int main() {
    int n, k, p, x;
    cin >> n >> k >> p;
    for(int i = 1 ; i <= n ; i++){
        cin >> x;
        sum[i] = (sum[i-1]+x)%p;
    }
    for(int i = 0 ; i <= p ; i++){
        for(int j = 0 ; j <= k ; j++){
            dp[i][j] = -1 << 30;
        }
    }
    dp[0][0] = 0;
    for(int i = 1 ; i <= n ; i++){
       for(int j = 1 ; j <= k ; j++){
           for(int mod = 0 ; mod <= p ; mod++){
               dp[sum[i]][j] = max(dp[sum[i]][j], dp[mod][j-1]+(sum[i]-mod+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、付费专栏及课程。

余额充值