数位dp|组合数学|差分emplace

lc2021

差分+sort

  • 事件处理: [start, end+1) ,对应事件为 (start, +1) 和 (end+1, -1) ,排序后遍历事件计算每个位置的亮度

  • 遍历统计:依次用前一个数 累加亮度变化值,即为变化点

实时更新最大亮度和对应位置,保证找到最亮且坐标最小的位置

class Solution {

public:

int brightestPosition(vector<vector<int>>& lights) {

vector<pair<int, int>> events;

for (auto& light : lights) {

int start = light0 - light1;

int end = light0 + light1 + 1; // 转换为左闭右开区间,方便差分

events.emplace_back(start, 1);

events.emplace_back(end, -1);

}

sort(events.begin(), events.end());

int maxBright = 0, currentBright = 0, ans = 0;

for (auto& pos, delta : events) {

currentBright += delta;

if (currentBright > maxBright) {

maxBright = currentBright;

ans = pos;

}

}

return ans;

}

};

emplace_back 适合需要直接在容器内构造对象的场景(可避免临时对象拷贝/移动,提升性能)

push_back 适合传递已有对象(或临时对象会被隐式构造时),逻辑更直观

sum++:需要"原地构造"选 emplace_back ,传递"现成对象"选 push_back++

lc3317

"拆分问题→分别计算各部分→相乘求和"

const int mod = 1e9 + 7;

const int MX = 1001;

long long sMXMX;

int init = \[\]()->int {

//i个人分成j组

s00 = 1;

for (int i = 1; i < MX; i++)

{

for (int j = 1; j < MX; j++)

{

sij = (si - 1j - 1 + (long long)j*si - 1j)%mod;

}

}

return 0;

}();

class Solution {

public:

int numberOfWays(int n, int x, int y) {

long long powY = 1;

long long A = 1;

long long ans = 0;

for (int i = 1; i <= min(n, x); i++)

{

A = (A*(x - i + 1)) % mod;

powY = powY * y%mod;

sni%=mod;

ans = (ans + ((A * powY)%mod*sni)%mod)%mod;

}

return ans;

}

};

一、问题在问什么

计算**"把 n 个人安排到 x 个节目里,每个节目至少1人,且每个节目有 y 种打分方式"的总方案数**

二、概念:第二类斯特林数

定义: S(n, i) 表示把 n 个不同的元素分成 i 个非空集合(集合无顺序)的方案数。

比如 S(3, 2) 就是把3个人分成2个非空组的方式数,结果是3(比如{1,2}和{3}、{1,3}和{2}、{2,3}和{1})

三、递推公式怎么来

++S(n, i) = S(n-1, i-1) + i * S(n-1, i)++

  • 第一种情况:第 n 个人自己 ++单独成一组++ → 剩下 n-1 个人分成 i-1 组,即 S(n-1, i-1) 。

  • 第二种情况:第 n 个人 ++加入之前++的 i 组中的某一组 → 有 i 种选择,剩下 n-1 个人分成 i 组,即 i * S(n-1, i) 。

四、代码

  1. 预处理斯特林数( init 函数)

s00 = 1; // 0个人分成0组,算1种方案

for (int i = 1; i < MX; i++)

for (int j = 1; j < MX; j++)

++sij = (si-1j-1 + (long long)j * si-1j) % mod;++

这部分是提前计算所有可能的 S(n, i) ,存在 s 数组里,方便后面直接用

  1. 计算最终答案( numberOfWays 函数)

我们要枚举"选 i 个节目来安排人",然后把三部分方案数相乘:

  • A :从 x 个节目中选 i 个有顺序的方案数(排列数 A(x, i) = x*(x-1)*...*(x-i+1) )

++- S(n, i) :把 n 个人分成 i 个非空组的方案数(用预处理好的 s 数组)++

  • powY :每个节目有 y 种打分方式, i 个节目就是 y^i 种

最后把所有 i 的情况加起来,就是最终答案。

int numberOfWays(int n, int x, int y) {

long long powY = 1;

long long A = 1;

long long ans = 0;

for (int i = 1; i <= min(n, x); i++) {

A *= (x - i + 1);

powY *= y;

ans += ++A * powY * sni;++

}

return ans;

}

五、举个例子

比如 n=3 , x=2 , y=2 :

  • 当 i=1 :选1个节目,把3人分成1组,打分方式 2^1 → 方案数 A(2,1)*S(3,1)*2^1 = 2*1*2 = 4 。

  • 当 i=2 :选2个节目,把3人分成2组,打分方式 2^2 → 方案数 A(2,2)*S(3,2)*4 = 2*3*4 = 24 。

  • 总答案: 4+24=28

lc600

数位dp

class Solution {

public:

int findIntegers(int n) {

vector<int> digits;

int x = n;

while (x > 0) {

digits.push_back(x % 2);

x /= 2;

}

reverse(digits.begin(), digits.end());

vector<vector<int>> memo(digits.size(), vector<int>(2, -1));

function<int(int, int, bool)> dfs = \&(int pos, int pre, bool isLimit) -> int {

++if (pos == digits.size())
return 1;
++

if (!isLimit && memopospre != -1) {

return memopospre;

}

int up = isLimit ? digitspos : 1;

int res = 0;

for (int d = 0; d <= up; d++) {

++bool newLimit = isLimit && (d == up);++

if (pre == 1 && d == 1) {

continue;

}

++res += dfs(pos + 1, d, newLimit);++

}

if (!isLimit) {

memopospre = res;

}

return res;

};

return dfs(0, 0, true);

}

};

  • 条件变了:比如"数位和是偶数",就把 pre 改成"前几位的数位和的奇偶性",然后判断最终和是否为偶。

  • 进制变了:比如十进制题目,就把拆数逻辑改成 %10 和 /10 , up 改成9即可。
相关推荐
如竟没有火炬8 分钟前
寻找峰值——二分
java·开发语言·数据结构·python·算法·散列表
noipp20 分钟前
推荐题目:洛谷 P1115 最大子段和
算法
Lumbrologist21 分钟前
【C++】零基础入门 · 第 17 节:多线程编程基础
java·c++·算法
轻闲一号机1 小时前
【语音】笔记
前端·笔记·算法
aWty_1 小时前
实分析入门(12)--可测函数
学习·数学·算法·实变函数
海砥装备HardAus1 小时前
无人机姿态解算中「重力矢量观测退化」机理与动态补偿技术
算法·无人机·飞控
广州灵眸科技有限公司1 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) 开发套件组装上电
网络·数据库·人工智能·算法·飞书
SuperHeroWu72 小时前
【算法】强化学习中奖励和损失函数的关系
算法·环境·强化学习·损失函数·奖励
voidmort2 小时前
9. 微调(Fine-tuning)的数学原理
人工智能·算法·机器学习
晚风吹红霞2 小时前
C++ stack 和 queue 完全指南:适配器模式与双端队列的奥秘
c++·算法·适配器模式