写在前面

思维难度不是很大的 DP,代码实现也很容易。

状态设计模式很套路,转移也很好理解。

算法思路

(因为 \(k\) 是常用的循环变量,下文中将题面中的模数改为 \(p\))

虽然要求的是模 \(p\) 结果为 \(0\) 的答案,显然这个结果受到选择的数的和可能受到模 \(p\) 任何余数的影响。因此不妨设计状态的时候想到用一维来表示答案模 \(p\) 的余数。

加上这一维之后就是一个背包模板了。

设 \(f_{i, j, t, k}\) 表示在第 \(i\) 行前 \(j\) 个数中选出 \(t\) 个数,且模 \(p\) 余 \(k\) 的最大答案。

那么显然有:

\[f_{i, j, t, k} = \max\{f_{i, j - 1, t, k}, f_{i, j - 1, t - 1, (k - a_{i, j})\bmod p} + a_{i, j}\}
\]

当可能存在多个(或多组) \(a_{i, j}\) 可能将同一个答案更新的时候,取最大值即可。

这样分别处理处每一行的答案之后,再设 \(g_{i, j}\) 表示在前 \(i\) 行中选择某些数使得其和模 \(p\) 的余数为 \(j\) 的最大答案。

那么显然有:

\[g_{i, j} = \max\{g_{i - 1, (j - k)\bmod p} + f_{i, m, t, k}\}
\]

Tips

  • 注意一些边界条件。

  • 处理的时候可能会出现不合法的状态,即在某一行内选出某些数,其和模 \(p\) 的余数根本不可能等于枚举的 \(k\) 的情况。这种处理方式归根到底还是因为它是从“选 \(0\) 个数的时候余数却不为 \(0\)”这个不合法状态转移来的。有个简单的处理方式,只需要将初值设为负无穷,然后将合法的初始状态(即选择数的个数和余数都为 \(0\))设为 \(0\) 就好了。

Code

  • 代码中对于每一行分别处理了这一行的 \(f\) 数组,并紧接着更新 \(g\) 数组,因此将 \(f\) 数组删掉了一维。

  • 代码中还使用了滚动数组。

#include <bits/stdc++.h>

using namespace std;

const int Maxn = 75;

namespace Read {
inline int read() {
int fh = 1, res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
return fh * res;
}
}
using namespace Read; int n, m, p; int a[Maxn][Maxn]; int f[2][Maxn][Maxn];
int g[2][Maxn]; int main() {
n = read(); m = read(); p = read();
for(register int i = 1; i <= n; ++i) {
for(register int j = 1; j <= m; ++j) {
a[i][j] = read();
}
}
memset(g, -0x3f, sizeof(g));
g[0][0] = 0;
for(register int i = 1; i <= n; ++i) {
memset(f, -0x3f, sizeof(f));
f[0][0][0] = 0;
for(register int j = 1; j <= m; ++j) {
f[j & 1][0][0] = 0;
for(register int t = 1; t <= min(j, m/2); ++t) {
for(register int k = 0; k < p; ++k) {
f[j & 1][t][k] = max(max(f[j & 1][t][k], f[j - 1 & 1][t][k]), f[j - 1 & 1][t - 1][(k - (a[i][j] % p) + p) % p] + a[i][j]);
}
}
}
for(register int j = 0; j < p; ++j) {
g[i & 1][j] = g[i - 1 & 1][j];
for(register int t = 0; t <= (m / 2); ++t) {
for(register int k = 0; k < p; ++k) {
g[i & 1][j] = max(g[i & 1][j], g[i - 1 & 1][(j - (f[m & 1][t][k] % p) + p) % p] + f[m & 1][t][k]);
}
}
}
}
printf("%d", g[n & 1][0]);
return 0;
}

CF1433F Zero Remainder Sum的更多相关文章

  1. C语言身份证信息查询系统(修改版)

    很久以前写了一个<C语言身份证信息查询系统>,如果你点击链接进去看了. 估计也会被我那磅礴大气的代码震惊到的,最近复习/学习文件操作,把代码改了改,算是对以前还不会文件操作的时候的愿望,哈 ...

  2. leetcode刷题第二天<两数相加>

    题目描述 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表 ...

  3. LeetCode67.二进制求和

    给定两个二进制字符串,返回他们的和(用二进制表示). 输入为非空字符串且只包含数字 1 和 0. 示例 1: 输入: a = "11", b = "1" 输出: ...

  4. jQuery使用工具集

    //jq-util.js$.extend({ Util:{ /* 浏览器 */ browser:{ IE: !!document.all, IE6: !!document.all && ...

  5. 回归 | js实用代码片段的封装与总结(持续更新中...)

      上一次更博还是去年10月28号了,截至今天已经有整整4个月没有更新博客了,没更新博客不是代表不学了,期间我已经用vue做了两个项目,微信小程序做了一个项目,只是毕竟找到工作了,想偷偷懒,你懂的. ...

  6. Codeforces Round #677 (Div. 3)

    F. Zero Remainder Sum || dp #include <cstdio> #include <algorithm> #include <cstring& ...

  7. Weekly Contest 139

    1071. Greatest Common Divisor of Strings For strings S and T, we say "T divides S" if and ...

  8. Codeforces - Educational Codeforces Round 5 - E. Sum of Remainder

    题目链接:http://codeforces.com/contest/616/problem/E 题目大意:给定整数n,m(1≤n,m≤1013), 求(n mod 1 + n mod 2 + ... ...

  9. codeforces 85D D. Sum of Medians 线段树

    D. Sum of Medians time limit per test 3 seconds memory limit per test 256 megabytes input standard i ...

随机推荐

  1. windows 10放大125%后字体模糊(已解决)、win10 文字放大之后变模糊 解决办法

    windows 10放大125%后字体模糊解决办法 百度搜索 "Windows10_DPI_FIX" 或者用这个地址下载下载地址   https://gallery.technet ...

  2. 容器编排系统K8s之访问控制--RBAC授权

    前文我们了解了k8s上的访问控制机制,主要对访问控制中的第一关用户认证做了相关说明以及常规用户的配置文件的制作,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/ ...

  3. python之logging 模块(下篇)

    四.日志处理流程(第二种日志使用方式) 上面简单配置的方法例子中我们了解到了logging.debug().logging.info().logging.warning().logging.error ...

  4. Synchronized 精讲

    1.简介 1.1 作用 在并发场景中,保证同一时刻只有一个线程对有并发隐患的代码进行操作 1.2 错误案例 需求:两个线程对 count 变量进行200000次循环增加,预期结果是400000次 pu ...

  5. 安装Android Studio遇到的问题

    1. 学习视频 视频链接:https://www.bilibili.com/video/BV1jW411375J?p=2 2. Android Studio1.5.1的下载地址: http://www ...

  6. 【SpringBoot1.x】SpringBoot1.x 缓存

    SpringBoot1.x 缓存 文章源码 JSR107 Java Caching 定义了 5 个核心接口,分别为: CachingProvider 定义了创建.配置.获取.管理和控制多个 Cache ...

  7. 【Redis3.0.x】发布订阅

    Redis3.0.x 发布订阅 基本命令 SUBSCRIBE channel [channel...] 订阅给定的一个或多个频道 PSUBSCRIBE pattern [pattern...] 订阅符 ...

  8. Docker安装MySQL,Redis,阿里云镜像加速

    Docker安装 虚拟化容器技术.Docker基于镜像,可以秒级启动各种容器.每一种容器都是一个完整的环境,容器之间相互隔离. 如果之前安装的有其他版本,卸载旧的版本. $ sudo yum remo ...

  9. servlet+jsp完成简单登录

    将用户在注册界面中的数据填充到数据库相对应的表格中.当用户再次登录时,从数据库中拿到相应的数据查询并与页面的数据做对比,判断是否登陆成功. 需要在HTML文件中将form表单上的action属性值设置 ...

  10. Linux学习笔记 | docker基本命令

    Docker的三大核心概念:镜像.容器.仓库 镜像:类似虚拟机的镜像.用俗话说就是安装文件. 容器:类似一个轻量级的沙箱,容器是从镜像创建应用运行实例,可以将其启动.开始.停止.删除.而这些容器都是相 ...