CF677DIV3-F:dp好题

题目大意:

给你一个n*m的二维矩阵。你每一行不能选择超过 ⌊ m 2 ⌋ \lfloor\frac{m}{2}\rfloor 2m个数。问你能够选择出的最大的能够被 k k k整除的数字和.
n , m , k ≤ 70 n,m,k \leq 70 n,m,k70

题目思路:

显然,每一行互相独立.所以可以分行dp. d p ( i , j , l ) dp(i,j,l) dp(i,j,l)代表前i个数,选择了j个数并且总和 m o d   k = l mod\ k=l mod k=l的最大价值.直接子序列模型dp即可.

然后得到一个 r e s [ i ] [ j ] res[i][j] res[i][j]代表第i行,总和 m o d   k = l mod\ k =l mod k=l的最大价值。每一行只能选择一个数,总共有n行。所以可以将其转换成分组背包,每组可不选的模型。再跑一遍背包dp即可。

然后这题有很多边界需要处理。
1. r e s res res数组的初始值如何定:
r e s [ i ] [ 0 ] = 0 res[i][0]=0 res[i][0]=0,其他的都等于-1代表不存在。
因为题目里说明了,每一行可以不选。所以 r e s [ i ] [ 0 ] res[i][0] res[i][0]需要赋值为0.

2. d p dp dp数组边界赋值为 -1.且 d p ( 0 , 0 , 0 ) = 0 dp(0,0,0)=0 dp(0,0,0)=0

3. f f f背包数组边界赋值为-1.且 f ( 0 , 0 ) = 0 f(0,0)=0 f(0,0)=0。因为 f f f的含义是“恰好”总和为k的概念。而不是最多。所以我们最开始的状态一定是要从 f ( 0 , 0 ) f(0,0) f(0,0)出发

#include
using namespace std;
#define ll long long
#define pii pair
#define pb push_back
#define mp make_pair
const int maxn = 70 + 5;
int dp[maxn][maxn][maxn];
int res[maxn][maxn];
int a[maxn];
int f[maxn][maxn];
int main()
{
    ios::sync_with_stdio(false);
    int n , m , mod; cin >> n >> m >> mod;
    memset(res , -1 , sizeof res);
    for (int i = 1 ; i <= n ; i ++) res[i][0] = 0;
    for (int t = 1 ; t <= n ; t++){
        for (int i = 1 ; i <= m; i++) cin >> a[i];
        memset(dp , -1 , sizeof dp);
        dp[0][0][0] = 0;
        for (int i = 1 ; i <= m ; i++){
            dp[i][0][0] = 0;
            for (int j = 1 ; j <= i ; j++){
                for (int k = 0 ; k < mod ; k++){
                    if (dp[i - 1][j][k] != -1)
                        dp[i][j][k] = dp[i - 1][j][k];
                    if (dp[i - 1][j - 1][(k - a[i]%mod + mod)%mod] != -1)
                        dp[i][j][k] = max (dp[i][j][k] , dp[i - 1][j - 1][(k - a[i]%mod + mod)%mod] + a[i]);
                }
            }
        }

        for (int i = 1 ; i <= (m / 2) ; i++)
            for (int j = 0 ; j < mod ; j++)
                res[t][j] = max (res[t][j] , dp[m][i][j]);
    }
    memset(f , -1 , sizeof f);
    for (int i = 0 ; i <= n ; i++) f[i][0] = 0;
    for (int i = 1 ; i <= n ; i++)
        for (int j = 0 ; j < mod ; j++)
            for (int k = 0 ; k < mod ; k++)
                if (f[i - 1][(j - k + mod)%mod] != -1 && res[i][k] != -1)
                    f[i][j] = max (f[i][j] , f[i - 1][(j - k + mod)%mod] + res[i][k]);
    cout << f[n][0] << endl;
    return 0;
}

你可能感兴趣的:(算法,动态规划)