[CF1433F] Zero Remainder Sum - dp
阿新 • • 發佈:2020-10-26
Description
給定一個大小為 \(n \times m\) 的矩陣,要求每行只能選取不超過一半的元素,使得所有選出元素的總和是 \(k\) 的倍數,且這個總和最大。求這個最大值。\(n,m,k \le 70\)。
Solution
考慮 dp,對於每一行 \(i\),首先預處理出 \(f[i][j][l][res]\) 表示在第 \(i\) 行中,從前 \(j\) 個數中選擇了 \(l\) 個數,和 \(\mod k = res\) 的最大和為多少。據此,我們可以對 \(f[i][*][*][res]\) 取 \(\max\) 得到 \(g[i][res]\),即從第 \(i\) 行中選不超過一半的數,且滿足總和 \(\mod k = res\)
設 \(h[i][res]\) 表示考慮前 \(i\) 行,從裡面選取若干個數(當然數量要合法),且滿足總和 \(\mod k = res\) 的限制條件時,能夠達到的最大的和是多少。利用 \(g[i][res]\) 顯然可以輕鬆計算。
#include <bits/stdc++.h> using namespace std; #define dbg(x) cout<<#x<<" = "<<x<<", " #define dbgn(x) cout<<#x<<" = "<<x<<endl const int N = 75; int a[N][N],n,m,k; int f[N][N][N][N],g[N][N],h[N][N]; signed main() { cin>>n>>m>>k; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { cin>>a[i][j]; } } memset(f,-1,sizeof f); memset(g,-1,sizeof g); memset(h,-1,sizeof h); for(int i=1;i<=n;i++) { f[i][0][0][0]=0; for(int j=1;j<=m;j++) { for(int r=0;r<k;r++) f[i][j][0][r]=f[i][j-1][0][r]; for(int l=1;l<=j;l++) { for(int r=0;r<k;r++) { int pos=(r-a[i][j]+100*k)%k; f[i][j][l][r]=f[i][j-1][l][r]; if(f[i][j-1][l-1][pos]!=-1) { f[i][j][l][r]=max(f[i][j][l][r],f[i][j-1][l-1][pos]+a[i][j]); } } } } } for(int i=1;i<=n;i++) { g[i][0]=0; for(int r=0;r<k;r++) { int ans=-1; for(int l=0;l<=m/2;l++) { ans=max(ans,f[i][m][l][r]); } g[i][r]=ans; } } h[0][0]=0; for(int i=1;i<=n;i++) { for(int r=0;r<k;r++) { for(int d=0;d<k;d++) { if(h[i-1][r]!=-1 && g[i][d]!=-1) { h[i][(r+d)%k]=max(h[i][(r+d)%k],h[i-1][r]+g[i][d]); } } } } cout<<h[n][0]<<endl; return 0; }