F. Zero Remainder Sum(思维+四维dp)

it2024-11-03  6

https://codeforces.com/contest/1433/problem/F


思路:感觉这道题锻炼dp挺好的。

定义:dp[i][j][l][r] : 在第i行第j位,选了l个数字,且此时模数为r的最大和

然后一般情况是第j个数字从j-1的对应转移过来,类似背包。但是这题这么做的话对于模数r的转移对应的要求的是(x%k+a[i][j]%k)%k=r的这个x。

方便一点的话转化成顺推。即考虑第j+1个数字选还是不选。


对于第i行,第j + 1个数字 1、选择第j + 1个数字 dp[i][j + 1][l][(r + a[i][j + 1]) % k] = dp[i][j][l - 1][r] + a[i][j+1]; 2、不选择第j + 1个数字 dp[i][j + 1][l][r] = dp[i][j][l][r];

同时这个题注意上下层的关系也要转移过来,因为下一层的状态选择需要上一层的决策。

然后每次更新下一层的第0位,就表示上一层模数为r时的最大值

dp[i + 1][0][0][r] = max(dp[i][j + 1][l][r], dp[i + 1][0][0][r]);   答案就是 dp[n + 1][0][0][0] : 第n+1行,第0位,取0个数字,模数为0的总和


#include<iostream> #include<vector> #include<queue> #include<cstring> #include<cmath> #include<map> #include<set> #include<cstdio> #include<algorithm> #define debug(a) cout<<#a<<"="<<a<<endl; using namespace std; const int maxn=80; typedef int LL; LL dp[maxn][maxn][maxn][maxn]; LL a[maxn][maxn]; int main(void) { cin.tie(0);std::ios::sync_with_stdio(false); LL n,m,k;cin>>n>>m>>k; for(LL i=1;i<=n;i++){ for(LL j=1;j<=m;j++) cin>>a[i][j]; } memset(dp,-1,sizeof(dp)); dp[1][0][0][0]=0; for(LL i=1;i<=n;i++){ for(LL j=0;j<m;j++) { for(LL l=0;l<=min(j+1,m/2);l++) { for(LL r=0;r<k;r++) { ///不取第j+1个数 dp[i][j+1][l][r]=max(dp[i][j+1][l][r],dp[i][j][l][r]); if(l&&dp[i][j][l-1][r]!=-1){ ///取第j+1个数 dp[i][j+1][l][(r+a[i][j+1])%k]=max(dp[i][j+1][l][(r+a[i][j+1])%k],dp[i][j][l-1][r]+a[i][j+1]); } } for(LL r=0;r<k;r++){ dp[i+1][0][0][r]=max(dp[i+1][0][0][r],dp[i][j+1][l][r]); } } } } cout<<max((LL)0,dp[n+1][0][0][0])<<endl; return 0; }

 

最新回复(0)