(背包式滚动dp+模运算)
题意:给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列。
题解:考虑到是子序列可以不连续,所以不能直接记录前缀和维护双指针做(子串做法)(即使这次题目数据太水,比赛中很多人当作子串暴力过了)不过我这里还是讲解正规做法。
首先可以先取走数组中被k整除的数,其余的数也都先%k,这样得到的数据都是k的范围内,对于题目数据很明显就是在暗示背包,然后就是剩下来的数进行模运算式的滚动背包了。
转换方程:dp[x ^ 1][(j + a[i]) % mod] = max(dp[x ^ 1][(j + a[i]) % mod], dp[x][j] + 1);
具体看代码~不好解释哈
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn = 1e7 + 50;
int dp[2][maxn], a[maxn];
int main()
{
int n, mod, xx, ans = 0, cnt = 0;
cin >> n >> mod;
for (int i = 0; i < n; i++)
{
scanf("%d", &xx);
if (xx%mod == 0)ans++;
else a[cnt++] = xx%mod;
}
int x = 0;
for (int i = 0; i < cnt; i++)
{
for (int j = 0; j < mod; j++)
if (j == 0 || dp[x][j])
dp[x ^ 1][(j + a[i]) % mod] = max(dp[x ^ 1][(j + a[i]) % mod], dp[x][j] + 1);
x ^= 1;
}
cout << ans + dp[x][0] << endl;
return 0;
}