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

leetcode-3405 统计恰好有k个相等相邻数组的个数

题目描述

给你三个整数 n ,m ,k 。长度为 n 的 好数组 arr 定义如下:

  • arr 中每个元素都在闭区间 [1, m] 中。
  • 恰好有k个下标 i(其中 1 <= i < n)满足 arr[i - 1] == arr[i] 。

请你返回可以构造出的好数组数目。

由于答案可能会很大,请你将它对 10^9 + 7 取余后返回。

过程

阶段

一开始当成一个动态规划问题如何理解

假定容纳n元素数组可选值范围m需要k满足题目条件子序列数组个数dp(n,m,k)

我们可以n-1情况递推n情况因为无非就是多了一个容纳元素格子这个格子可以前者相同元素这种元素我们称之A元素可以构造题目需要k子序列一个另一种情况就是不选A元素而是选择普通元素不会创造相等邻接子序列元素我们称之X元素除了数组一个元素(必定为X元素,因为第一个元素前面没有任何元素可以接)m取值以外其他X元素只有m-1取值因为不能前面元素相同(否则就变成了A类元素)

所以当我们开始递推

当新加入的格子选择A类元素时,不同的数组个数有 dp(n - 1,m,k - 1) 个,因为A类元素只有一种取值。

新加入格子选择X元素不同数组个数dp(n - 1, m , k) * (m - 1)

所以递推公式

dp(n,k) = dp(n-1,k-1) + dp(n - 1, k) * (m - 1)

这里因为m始终不变所以省略不写本质是一个二维dp

k = 0dp(n,0) m * pow(m - 1, n -1)数组排列组合知识得到

因此便可以按顺序

于是有了第一种代码

int countGoodArrays(int n, int m, int k) {
	int search[n][k + 1] = {};
	unsigned long long resolves = m;for(int i = 0;i < n; i++){
		search[i][0] = resolves;
		resolves = (resolves * (m - 1)) % K;}for(int i = 1;i < n;i ++)for(int q = 1;q < k + 1;q ++){
		search[i][q] = (search[i-1][q] * (m - 1) + search[i-1][q-1]) % K;}
	return search[n-1][k];
}

阶段

代码内存可以优化

因为dp数组前面每一行完了后面用不到了所以可以删掉所以引入轮转优化代码

int countGoodArrays2(int n, int m, int k) {
	vector<int> tmp(k + 1, 0),ret;unsigned long long resolves = m;
	tmp[0] = resolves;	for(int i = 1;i < n; i++){
		resolves = (resolves * (m - 1)) % K;
		ret.emplace_back(resolves);for(int q = 0;q < k;q ++)
		ret.emplace_back(((unsigned long long)tmp[q + 1] * (m - 1) + tmp[q]) % K);
		tmp.clear();
		ret.swap(tmp);
		ret.reserve(k + 1);}return tmp[k];
}

通过每一次swap使得最终dp数组占用只有两行

阶段三

后面提测发现题目n,m,k都会很大导致时间空间基本怎么样都会这里怀疑方法有问题,又仔细一下

新思路

给定大小n数组除了数组第一个元素必须X元素以外剩下可以A元素可以X元素那么这里可以组合Cn - 1位置里面选择k位置填充A元素剩下都是X元素但是关键组合需要计算阶乘这里数值计算很大所以相当于本题考察大数组合数计算注意题目有说

由于答案可能会很大,请你将它对 10^9 + 7 取余后返回。

这里知道要用费马小定理结合逆元计算组合数

运算除法可以改成逆元乘法,这是模运算的性质。

由于题目MOD素数所以可以费马小定理计算一个逆元

当p是素数时,a逆元可以转换a高阶所以幂运算我们通过快速幂运算完成这一点

long long qpow(long long a, long long b) {long long res = 1;
    a %= K;while (b > 0) {if (b & 1) res = res * a % K;
        a = a * a % K;
        b >>= 1;}return res;
}

这样我们就能求出每个逆元由于输入n > m所以最高需要阶乘就是n!n最大1e5所以我们通过数组保存计算阶乘

for(int i = 0; i <= MX; i ++){if(i == 0){
			pw.emplace_back(1);
			continue;}
		pw.emplace_back((unsigned long long)pw[pw.size() - 1] * i % K);}

然后通过逆元递推公式

得到对应每个阶乘逆元数组

anv[MX] = qpow(pw[MX],K-2);for(int i = MX - 1;i >= 0; i--){
		anv[i] = (i + 1) * (unsigned long long)anv[i + 1] % K;}

最后代入即可求得组合

组合出来之后对于每个A元素取值只有一种可能对于第一个X元素取值有m种可能后面每个X元素m - 1可能起来即可这里涉及又可以qpow快速函数

x = (x * qpow(m-1,n - k -1)) % K;

最终代码

#include <vector>
vector<int> anv,pw;
class Solution {
public:unsigned long long K = 1000000000 + 7;int MX = 1e5;
long long qpow(long long a, long long b) {long long res = 1;
    a %= K;while (b > 0) {if (b & 1) res = res * a % K;
        a = a * a % K;
        b >>= 1;}return res;
}int C(int m, int n)
{if(!n || m == n) return 1;if(n > m) return 0;return (unsigned long long)pw[m] * anv[n] % K * anv[m - n] % K;
}
int countGoodArrays(int n, int m, int k) {if(pw.empty()){
        pw.reserve(MX + 1);
        anv.reserve(MX + 1);for(int i = 0; i <= MX; i ++){if(i == 0){
			pw.emplace_back(1);continue;}
		pw.emplace_back((unsigned long long)pw[pw.size() - 1] * i % K);}
	anv[MX] = qpow(pw[MX],K-2);for(int i = MX - 1;i >= 0; i--){
		anv[i] = (i + 1) * (unsigned long long)anv[i + 1] % K;}}int ret = 0;unsigned long long resolves = C(n - 1, k);unsigned long long x = m;
	x = (x * qpow(m-1,n - k -1)) % K;
	ret = (resolves * x) % K;return ret;
}
};

http://www.lryc.cn/news/571881.html

相关文章:

  • C2远控篇CC++InlineHook挂钩动态API调用突破内存加密导入表检测
  • JSX 详解:React 的核心语法
  • Meta V-JEPA 2:革命性的视频联合的世界模型
  • OpenStack体验
  • 深入理解 MySQL 事务:保障数据操作的原子性与一致性
  • MySQL 库操作和表操作
  • 【51单片机】8. 矩阵LED显示自定义图案、动画
  • Mac m1 通过docker镜像安装kafka
  • 【GateWay】和权限验证
  • RKNN开发环境搭建3-RKNN Model Zoo 板载部署以Whisper为例
  • 【AI作画】用comfy ui生成漫画风图画
  • spring-webmvc @InitBinder 典型用法
  • 架构优化——submodule转为subtree
  • ES 索引加载 vs BulkLoad
  • ArcGIS中利用泰森多边形法分析站点与流域占比
  • docker拉取Elasticsearch和Kibana
  • python3:线程管理进程
  • C++ 进阶:深入理解虚函数、继承与多态
  • 管件接头的无序抓取
  • C++11中alignof和alignas的入门到精通指南
  • 大语言模型指令集全解析
  • ATX电源
  • Java 淘宝商品详情接口实战解析
  • 小白成长之路-Rsync+sersync实现数据实时同步
  • 基于集体智能长尾识别的超声乳腺病变亚型分类|文献速递-深度学习医疗AI最新文献
  • 从零接入高德路径规划2.0:实现精准物流距离计算实战
  • FPGA基础 -- Verilog行为级建模之initial语句
  • C++11 移动语义详解
  • 基于大模型的胆囊结石全周期诊疗方案研究报告
  • vue3 javascript 多字段求和技巧