Zero Remainder Sum
题目链接
题目大意
相信看题解的都是看懂题目的(主要是懒得打字了)
解题思路
一看就是dp题,我们按照一行一行从上到下、从左到右的顺序来求解、我们用dp[i][j][cnt][rem] 表示选到第i行、第j列并且第i行已经选择cnt个元素且余数为rem的时候的最大值。那么现要进行初始化,dp[0][0][0][0]=0,其余的都是-inf
然后就是递归式
对于每一行的每一列的元素假设为a[i][j]来说,都有选择(前题是cnt+1<=m/2)或者不选的可能。如果选择这个的话,就向后更新,即
nrem=(rem+a[i][j])%k; dp[i][j+1][cnt+1][nrem]=max(dp[i][j+1][cnt+1][nrem],dp[i][j][cnt][rem]+a[i][j]);
如果不选择的话,就有
dp[i][j+1][cnt][nrem]=max(dp[i][j+1][cnt][nrem],dp[i][j][cnt][rem]+a[i][j]);
但是当我们遍历到最后一个元素的时候,就有些不同,因为我们第i行的答案受第i-1行的影响(从上到下),因此我们遍历到每一行的最后一个元素的时候,要将其更新到下一行而不是再外后,除此之外,如果这个值选取的话,对下行的cnt值并没有影响,因此也就不用进行cnt+1次操作。最后的答案就是dp[n][0][0][0]
#include<bits/stdc++.h> using namespace std; const int maxn=70; int dp[maxn][maxn][maxn][maxn]; int a[maxn][maxn]; const int inf=-0x3f3f3f; int main() { int n,m,k; cin>>n>>m>>k; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { cin>>a[i][j]; } } for(int i=0;i<maxn;i++) { for(int j=0;j<maxn;j++) { for(int cnt=0;cnt<maxn;cnt++) { for(int rem=0;rem<maxn;rem++) { dp[i][j][cnt][rem]=inf; } } } } dp[0][0][0][0]=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { for(int cnt=0;cnt<m/2+1;cnt++) { for(int rem=0;rem<k;rem++) { if(dp[i][j][cnt][rem]==-inf) continue; int ni=(j==m-1?i+1:i); int nj=(j==m-1?0:j+1); if(i!=ni) //显示不选择的情况 表示切换到下一行 { dp[ni][nj][0][rem]=max(dp[ni][nj][0][rem],dp[i][j][cnt][rem]); } else{ dp[ni][nj][cnt][rem]=max(dp[ni][nj][cnt][rem],dp[i][j][cnt][rem]); } if(cnt+1<=m/2) { int nrem=(a[i][j]+rem)%k; if(i!=ni) { dp[ni][nj][0][nrem]=max(dp[ni][nj][0][nrem],dp[i][j][cnt][rem]+a[i][j]); } else{ dp[ni][nj][cnt+1][nrem]=max(dp[ni][nj][cnt+1][nrem],dp[i][j][cnt][rem]+a[i][j]); } } } } } } cout<<dp[n][0][0][0]<<endl; return 0; }