Description
给定一个大小为 (n imes 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;
}