转自:https://www.cnblogs.com/widsom/p/8857777.html 略有修改
题目大意:
n个数,划分为k段,每一段的和mod p,求出每一段的并相加,求最大是多少
基本思路:
区间dp无疑
dp[i][j] 表示到第i个位置为止,分成j段的最大值
dp[i][j]=max(dp[i][j],dp[l][j-1]+(sum[i]-sum[l])%p) 0<l<i
由于n很大,p很小,所以优化一下
dp[i][j] 表示sum取模p后为i,分成j段的最大值
dp[i][j] = max(dp[i][j],dp[l][j-1] + (sum[i] - l) % p) 0<= l < p
代码如下:
#include<cstdio> #include<cmath> #include<cstring> #include<iostream> #include<string> #include<algorithm> #include<queue> #include<vector> #include<set> using namespace std; typedef long long ll; typedef long long LL; typedef pair<int,int> pii; const int inf = 0x3f3f3f3f; const int maxn = 20000+10; const ll mod = 1e9+9; int dp[110][60]; int a[maxn]; int main(){ int n,k,p; scanf("%d%d%d",&n,&k,&p); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); a[i]+=a[i-1]; a[i]%=p; } for(int i=0;i<p;i++){ for(int j=0;j<=k;j++){ dp[i][j]=-inf; } } dp[0][0]=0; for(int i=1;i<=n;i++){ for(int j=k;j>=1;j--){ for(int l=0;l<p;l++){ dp[a[i]][j]=max(dp[a[i]][j],dp[l][j-1]+(a[i]-l+p)%p); } } } printf("%d ",dp[a[n]][k]); return 0; }