当前位置: 首页 > news >正文

状态设计_多重集排列数_剪枝

 

“预测未来的最好方法是创造它”

lc3539

预处理阶乘与逆阶乘+memo

核心是计算在给定条件下,从数组中选数组合的“魔力和” 
先提前算好数学工具(阶乘)

递归+记忆化的方式,枚举所有可能的选数方案,最后按规则算出结果

const int MOD = 1'000'000'007;
const int MX = 31;

long long F[MX]; // F[i] = i!
long long INV_F[MX]; // INV_F[i] = i!^-1

long long pow(long long x, int n) {
long long res = 1;
for (; n; n /= 2) {
if (n % 2) {
res = res * x % MOD;
}
x = x * x % MOD;
}
return res;
}

auto init = [] {
F[0] = 1;
for (int i = 1; i < MX; i++) {
F[i] = F[i - 1] * i % MOD;
}

    INV_F[MX - 1] = pow(F[MX - 1], MOD - 2);
for (int i = MX - 1; i; i--) {
INV_F[i - 1] = INV_F[i] * i % MOD;
}
return 0;
}();

class Solution {
public:
int magicalSum(int m, int k, vector<int>& nums) {
int n = nums.size();
vector pow_v(n, vector<int>(m + 1));
for (int i = 0; i < n; i++) {
pow_v[i][0] = 1;
for (int j = 1; j <= m; j++) {
pow_v[i][j] = 1LL * pow_v[i][j - 1] * nums[i] % MOD;
}
}

        vector memo(n, vector(m + 1, vector(m / 2 + 1, vector<int>(k + 1, -1))));
auto dfs = [&](this auto&& dfs, int i, int left_m, int x, int left_k) -> int {
int c1 = popcount((uint32_t) x);
if (c1 + left_m < left_k) { // 可行性剪枝
return 0;
}
if (i == n || left_m == 0 || left_k == 0) { // 无法继续选数字
return left_m == 0 && c1 == left_k;
}
int& res = memo[i][left_m][x][left_k]; // 注意这里是引用
if (res != -1) {
return res;
}
res = 0;
for (int j = 0; j <= left_m; j++) { // 枚举 I 中有 j 个下标 i
// 这 j 个下标 i 对 S 的贡献是 j * pow(2, i)
// 由于 x = S >> i,转化成对 x 的贡献是 j
int bit = (x + j) & 1; // 取最低位,提前从 left_k 中减去,其余进位到 x 中
int r = dfs(i + 1, left_m - j, (x + j) >> 1, left_k - bit);
res = (res + 1LL * r * pow_v[i][j] % MOD * INV_F[j]) % MOD;
}
return res;
};
return 1LL * dfs(0, m, 0, k) * F[m] % MOD;
}
};

 

lc3343

class Solution {
private:
// 定义模数为10^9+7(题目要求的取模值)
const int MOD = 1e9 + 7;
typedef long long ll; // 定义长整型别名,防止计算溢出

    // 组合数学工具:存储阶乘和逆阶乘(用于计算排列组合数)
vector<int> fac;    // fac[n] = n!(n的阶乘)
vector<int> invFac; // invFac[n] = (n!)^(-1)(阶乘的逆元)

    // 快速幂算法(用于高效计算幂取模)
ll Pow(ll a, int b) {
ll res = 1;
while (b) {
if (b & 1) { // 当指数是奇数时
res = res * a % MOD;
}
a = a * a % MOD; // 底数平方
b >>= 1;         // 指数减半
}
return res;
}

    // 计算逆元(基于费马小定理,MOD是质数时可用)
ll Inv(ll a) { return Pow(a, MOD - 2); }

    // 初始化阶乘和逆阶乘数组
void Init(int n) {
fac.resize(n + 1);
invFac.resize(n + 1);

// 计算阶乘
fac[0] = 1; // 0! = 1
for (int i = 1; i <= n; i++) {
fac[i] = (ll)fac[i - 1] * i % MOD; // 递推计算阶乘
}

// 计算逆阶乘(从后往前递推)
invFac[n] = Inv(fac[n]); // 先算最大的阶乘的逆元
for (int i = n; i > 0; i--) {
// 根据 (n!)^(-1) = (n+1) * ((n+1)!)^(-1) 递推
invFac[i - 1] = (ll)invFac[i] * i % MOD;
}
}

    // 安全的加法取模(防止溢出)
void Addi(int& a, int b) {
a += b;
if (a >= MOD) { // 如果超过模数就减去
a -= MOD;
}
}

public:
int countBalancedPermutations(string num) {
int sum = 0; // 计算所有数字的总和
int n = num.size();
for (const auto& c : num) {
sum += c - '0'; // 将字符转为数字相加
}

// 重要判断:总和必须是偶数才能平分
if (sum & 1) {
return 0; // 奇数总和直接返回0
}

// 初始化组合数学工具(需要计算排列数)
Init(n);

/******************** 核心动态规划部分 ********************/
// dp[i][j] 表示:选出的数字和为i,且选了j个数字的方案数
// 目标状态是dp[sum/2][n/2],即选出总和为总和一半,且数量为总位数一半的情况
vector<vector<int>> dp(sum / 2 + 1, vector<int>((n + 2) / 2, 0));
dp[0][0] = 1; // 初始状态:和为0,选0个数字的方案数是1

array<int, 10> cnt = {}; // 统计每个数字(0-9)的出现次数

// 动态规划转移(类似背包问题)
for (const auto& c : num) {
int val = c - '0'; // 当前处理的数字值
cnt[val]++; // 记录该数字出现的次数

// 逆向遍历防止重复计算(每个数字只能选一次)
for (int i = sum / 2; i >= val; i--) {   // 遍历可能的和
for (int j = n / 2; j > 0; j--) {    // 遍历可能的选取数量
// 状态转移方程:选当前数字的情况下,方案数累加
Addi(dp[i][j], dp[i - val][j - 1]);
}
}
}

/******************** 组合计算结果 ********************/
// 计算最终排列数 = 方案数 × 奇数位排列 × 偶数位排列 / 重复数字的排列
ll res = (ll)dp[sum/2][n/2] * fac[n/2] % MOD; // 奇数位的排列方式
res = res * fac[(n+1)/2] % MOD; // 偶数位的排列方式

// 处理重复数字的情况(除以重复次数的阶乘)
for (const auto& x : cnt) {
res = res * invFac[x] % MOD; // 相当于除以x!(乘以逆元)
}

return res;
}
};

 

lc1275

 

http://www.dtcms.com/a/474599.html

相关文章:

  • adt-bundle-windows
  • Bootstrap 5入门指南
  • 奥林巴斯读片软件OlyVIA 2.9 下载安装教程怎样下载安装图文教程
  • 18006.STM32通过SPI读取LAN9253数据
  • 无锡网站建设维护梅州建站教程
  • SQLSugar和EF都是数据优先吗?
  • 企业网站功能是什么百度怎么发布网站
  • 八股-2025.10.12
  • Eigen 曲线拟合之四阶多项式(有界约束,投影法)
  • 深度剖析 C++ 之 vector(下)篇
  • Vue计算属性与监视
  • 零基础学AI大模型之解析器PydanticOutputParser
  • Linux 命令 —— 常用命令总结
  • 【AI论文】大型推理模型能从有缺陷的思维中更好地习得对齐能力
  • 网站服务器费用wordpress手赚推广
  • 24ICPC昆明站补题
  • 口碑好的聊城网站建设设计软件网站
  • 五种编程语言比较选择最适合您项目的工具
  • 商城网站开发项目分工公司网页背景图
  • 第六章:并发编程—Go的杀手锏
  • 网站建设内部流程图定制开发网站 推广
  • 衡石科技HENGSHI SENSE 6.0:重塑企业级BI的技术范式
  • 西安便宜网站建设品牌网十大品牌排行榜
  • OpenSIPS call_center 模块测试
  • 深度学习周报(10.6~10.12)
  • 易语言实现多文件选择对话框模块详解
  • 电子商务网站建设与综合实践如何翻译wordpress主题
  • Java基础--集合复习知识点
  • spdlog讲解
  • 怎样用vps做网站超级优化