题目 | 满意的集合
牛客小白月赛43
E - 满意的集合
https://ac.nowcoder.com/acm/contest/11220/E
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
有数字 $1\sim9$,每个数字的个数分别为 $cnt_1,cnt_2,cnt_3,...,cnt_9$ 。计算出“满意的集合“的个数。
"满意的集合" 定义:选出的数存在一种排列方式,其拼接起来后表示的十进制整数,能被 $3$ 整除,例如集合 $\{3,3,6\}$ $($包含了 $2$ 个数字 $3,1$ 个数字 $6$ $)$,可以有排列 $\{6,3,3\}$ 代表十进制下的整数 $633$,能被 $3$ 整除。
两个集合相同,当且仅当集合元素个数相同,且排序后对应数字相同,例如 $\{3,3,6\}$ 和 $\{3,6,3\}$ 是同样的集合。
空集合看作 $0$ ,是合法的,答案对 $1e9+7$ 取模。
输入描述:
输入一行,包括 $9$ 个整数 $cnt_1,cnt_2,cnt_3,...,cnt_9$,分别表示数字 $1\sim9$ 的个数,$0\le cnt_i\le 1e9$。
输出描述:
输出一行,表示”满意的集合”的个数,答案对 $1e9+7$ 取模。
示例1
输入
1 1 1 0 0 0 0 0 0
输出
4
说明
全部的数字集合 $\{1,2,3\}$。可能的集合有$\{\},\{1\},\{2\},\{3\},\{1,2\},\{1,3\},\{2,3\},\{1,2,3\}$。其中“满意的集合“有 $\{1,2\},\{3\},\{1,2,3\},\{\}$ 共 $4$ 个。
我的笔记
分析
首先简化题意,将“排列 $\{6,3,3\}$ 代表十进制下的整数 $633$,能被 $3$ 整除”,转化为“$\{6,3,3\}$ 中元素每一位的和能被 $3$ 整除”。
然后进行分析,本题可使用动态规划算法,将其转化为递推。递推第 0 层就是数字集合里不选任何数的情况,第 1 层就是数字集合里只选 1 的情况,第 2 层就是数字集合里只选 1、2 的情况,第 3 层就是数字集合里只选 1、2、3 的情况……(每层递推都允许不选任何数)
然后得到以下 DP 写法:
// TLE Version DP
for (int i = 1; i <= 9; i++) // 递推层数,直到推到第9层,即获得符合题意的答案
{
for (int j = 0; j <= cnt[i]; j++) // 遍历选0个i、1个i、2个i……直到全选的所有情况
{
for (int k = 0; k < 3; k++) // 递推模为0、为1、为2的结果
{
dp[i][(j * i + k) % 3] += dp[i - 1][k];
dp[i][(j * i + k) % 3] %= MOD; // MOD为1e9+7
}
}
}
但可以发现,该方法时间复杂度极高,为 $O(\sum_{n=1}^9cnt_n)$ ,一定会 TLE
进一步分析,每个数选 $k,k+3,k+6,\cdots,k+3*x(k\in\{0,1,2\})$ 时,和与 3 的模数是相等的。如:
(0 * 2) % 3 = 0, (1 * 2) % 3 = 2, (2 * 2) % 3 = 1,
(3 * 2) % 3 = 0, (4 * 2) % 3 = 2, (5 * 2) % 3 = 1……
这样我们就能把 $cnt_1$ 种情况转化为 3 种情况,即选 0 个、选 1 个、选 2 个,然后剩余情况都与这三个相等,我们统计一下这三种情况的个数,修改掉上面的 DP 中的第二层循环,就能将时间复杂度降低至 $O(9*3*3)$
源码
#include <bits/stdc++.h>
#define MOD 1000000007
using namespace std;
int main(void)
{
int cnt[10];
for (int i = 1; i <= 9; i++)
{
cin >> cnt[i];
}
long long dp[10][3];
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= 9; i++)
{
int mod_1 = (0 * i) % 3, mod_2 = (1 * i) % 3, mod_3 = (2 * i) % 3;
int cnt_mod_1 = cnt[i] / 3 + 1, cnt_mod_2 = (cnt[i] + 1) / 3, cnt_mod_3 = (cnt[i] + 2) / 3;
for (int j = 0; j < 3; j++)
{
dp[i][(j + mod_1) % 3] += dp[i - 1][j] * cnt_mod_1;
dp[i][(j + mod_2) % 3] += dp[i - 1][j] * cnt_mod_2;
dp[i][(j + mod_3) % 3] += dp[i - 1][j] * cnt_mod_3;
dp[i][(j + mod_1) % 3] %= MOD;
dp[i][(j + mod_2) % 3] %= MOD;
dp[i][(j + mod_3) % 3] %= MOD;
}
}
cout << dp[9][0] << endl;
return 0;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi