【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字

2024-02-15 15:36

本文主要是介绍【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者推荐

视频算法专题

本文涉及知识点

动态规划汇总

LeetCode:1012. 至少有 1 位重复的数字

给定正整数 n,返回在 [1, n] 范围内具有 至少 1 位 重复数字的正整数的个数。
示例 1:
输入:n = 20
输出:1
解释:具有至少 1 位重复数字的正数(<= 20)只有 11 。
示例 2:
输入:n = 100
输出:10
解释:具有至少 1 位重复数字的正数(<= 100)有 11,22,33,44,55,66,77,88,99 和 100 。
示例 3:
输入:n = 1000
输出:262
提示:
1 <= n <= 109

动态规划

动态规划的状态表示

自定义状态mask的含义:如果(1<<i)&mask 表示i已经使用,i取值范围[0,9]。每个状态有两个值:first,不含重复数字的数量;second,含重复数字的数量。

动态规划的转移方程

前一位的自定义状态mask,当前数字index。newMask = mask | ( 1 << index) m代码mask,m1代表newMask。如果之前的已经包括当前数字,则全部数字都是重复数字;否则,之前是重复数字,现在仍然是重复数字,之前不是重复数字,现在也不是。
{ d p [ m 1 ] . s e c o n d + = p r e [ m ] . f i r s t + p r e [ m ] . s e c o n d m = = m 1 d p [ m 1 ] . f i r s t + = p r e [ m ] . f i r s t d p [ m 1 ] . s e c o n d + = p r e [ m ] . s e c o n d e l s e \begin{cases} dp[m1].second += pre[m].first + pre[m].second & m==m1\\ dp[m1].first+= pre[m].first \quad dp[m1].second += pre[m].second & else \\ \end{cases} {dp[m1].second+=pre[m].first+pre[m].seconddp[m1].first+=pre[m].firstdp[m1].second+=pre[m].secondm==m1else

动态规划的初始值

对每个合法数字index。 pre[i<<index].first =1 。

动态规划的填表顺序

按封装类是按从高位到低位处理的。

动态规划的返回值

所有状态 second之和。

代码

核心代码

template<class ELE, class ResultType, ELE minEle, ELE maxEle>
class CLowUperr
{
public:CLowUperr(int iResutlCount) :m_iResutlCount(iResutlCount){}void Init(const ELE* pLower, const ELE* pHigh, int iNum){m_vPre.assign(4, vector<ResultType>(m_iResutlCount));if (iNum <= 0){return;}InitPre(pLower, pHigh);iNum--;while (iNum--){pLower++;pHigh++;vector<vector<ResultType>> dp(4, vector<ResultType>(m_iResutlCount));OnInitDP(dp);//处理非边界for (auto tmp = minEle; tmp <= maxEle; tmp++){OnEnumOtherBit(dp[0], m_vPre[0], tmp);}//处理下边界OnEnumOtherBit(dp[1], m_vPre[1], *pLower);for (auto tmp = *pLower + 1; tmp <= maxEle; tmp++){OnEnumOtherBit(dp[0], m_vPre[1], tmp);}//处理上边界OnEnumOtherBit(dp[2], m_vPre[2], *pHigh);for (auto tmp = minEle; tmp < *pHigh; tmp++){OnEnumOtherBit(dp[0], m_vPre[2], tmp);}//处理上下边界if (*pLower == *pHigh){OnEnumOtherBit(dp[3], m_vPre[3], *pLower);}else{OnEnumOtherBit(dp[1], m_vPre[3], *pLower);for (auto tmp = *pLower + 1; tmp < *pHigh; tmp++){OnEnumOtherBit(dp[0], m_vPre[3], tmp);}OnEnumOtherBit(dp[2], m_vPre[3], *pHigh);}m_vPre.swap(dp);}}/*ResultType Total(int iMinIndex, int iMaxIndex){ResultType ret;for (int status = 0; status < 4; status++){for (int index = iMinIndex; index <= iMaxIndex; index++){ret += m_vPre[status][index];}}return ret;}*/
protected:const int m_iResutlCount;void InitPre(const ELE* const pLower, const ELE* const pHigh){for (ELE cur = *pLower; cur <= *pHigh; cur++){int iStatus = 0;if (*pLower == cur){iStatus = *pLower == *pHigh ? 3 : 1;}else if (*pHigh == cur){iStatus = 2;}OnEnumFirstBit(m_vPre[iStatus], cur);}}virtual void OnEnumOtherBit(vector<ResultType>& dp, const vector<ResultType>& vPre, ELE curValue) = 0;virtual void OnEnumFirstBit(vector<ResultType>& vPre, const ELE curValue) = 0;virtual void OnInitDP(vector<vector<ResultType>>& dp){}vector<vector<ResultType>> m_vPre;
};class CCharLowerUper : public CLowUperr<char, pair<int, int>, '0', '9'>
{
public:CCharLowerUper():CLowUperr(1<<10){}int Total(){return Total(0, m_iResutlCount-1);}int Total(int iMinIndex, int iMaxIndex){int ret = 0;for (int index = iMinIndex; index <= iMaxIndex; index++){int cur = 0;for (int status = 0; status < 4; status++){cur += m_vPre[status][index].second;}ret += cur;}return ret;}
protected:virtual void OnEnumFirstBit(vector<pair<int, int>>& vPre, const char curValue){const int index = curValue - '0';vPre[1 << index].first = 1;	}virtual void OnEnumOtherBit(vector<pair<int, int>>& dp, const vector<pair<int, int>>& vPre, char curValue){const int index = curValue - '0';for (int i = 0; i < m_iResutlCount; i++){const int iNewMask = (1 << index) | i;if (iNewMask == i ){dp[iNewMask].second += vPre[i].first + vPre[i].second;}else{dp[iNewMask].first += vPre[i].first;dp[iNewMask].second +=  vPre[i].second;}}}
};class Solution {
public:int numDupDigitsAtMostN(int n) {const string strN = std::to_string(n);const int len = strN.length();int iRet = 0;for (int i = 1; i < len; i++){CCharLowerUper lu;lu.Init(("1" + string(i - 1, '0')).c_str(), string(i, '9').c_str(), i);iRet += lu.Total();}CCharLowerUper lu;lu.Init(("1" + string(len - 1, '0')).c_str(), strN.c_str(), len);iRet += lu.Total();return iRet;}
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{	int n;{Solution sln;n = 20;auto res = sln.numDupDigitsAtMostN(n);Assert(res, 1);}{Solution sln;n = 100;auto res = sln.numDupDigitsAtMostN(n);Assert(res, 10);}{Solution sln;n = 1000;auto res = sln.numDupDigitsAtMostN(n);Assert(res, 262);}
}

2023年1月版

int GetNotRepeateNum(int len, int iHasSel)
{
if (0 == len)
{
return 1;
}
if ((0 == iHasSel) && (1 == len))
{
return 10;
}
int iRet = 1;
if (iHasSel > 0)
{
for (int tmp = 10 - iHasSel; (tmp >= 2)&& len ; tmp–,len–)
{
iRet *= tmp;
}
}
else
{
iRet *= 9;
len–;
for (int tmp=9; (tmp>=2)&&len; len–,tmp–)
{
iRet *= tmp;
}
}
return iRet;
}
class Solution {
public:
int numDupDigitsAtMostN(int n) {
string s = std::to_string(n);
int iBitLen =s.length();
int iNotRepeatNum = 0;
for (int i = 1; i < iBitLen; i++)
{
iNotRepeatNum += GetNotRepeateNum(iBitLen-i, 0);
}
std::set setHasSel;
//位数相同,但最高为比n小
for (int i = 0; i < iBitLen; i++)
{
int iNum = s[i] - ‘0’;
if (1 + i == iBitLen)
{
iNum++;//最后一位可以相等
}
int iLessNum = iNum - std::distance(setHasSel.begin(), setHasSel.lower_bound(iNum));
if (0 == i && 1 != iBitLen)
{
iLessNum–;
}
if (iLessNum > 0 )
{
iNotRepeatNum += iLessNum * GetNotRepeateNum(iBitLen - i - 1, i + 1);
}
if (setHasSel.count(iNum))
{
break;
}
setHasSel.insert(iNum);
}
//扣掉0
return n - iNotRepeatNum + 1;
}
};

2023年8月版

class Solution {
public:
int numDupDigitsAtMostN(int n) {
auto str = std::to_string(n);
for (int i = 1; i < str.length(); i++)
{
Do(string(i, ‘9’));
}
Do(str);
return m_iRet;
}
void Do(const string& strUp)
{
int pre[2][1024] = { 0 };
{
const int iMax0 = strUp[0] - ‘0’;
for (int i = 1; i <= iMax0; i++)
{
pre[i == iMax0][1 << i ]++;
}
}
{
for (int i = 1; i < strUp.length(); i++)
{
int dp[2][1024] = { 0 };
//处理不在边界
for (int j = 0; j < 10; j++)
{
for (int pr = 0; pr < 1024; pr++)
{
int iMask = (pr & (1 << j)) ? 0 : (pr | (1 << j));
if (pr == 0)
{
iMask = 0;
}
dp[0][iMask] += pre[0][pr];
}
}
const int iMaxI = strUp[i] - ‘0’;
//处理在边界
for (int j = 0; j <= iMaxI; j++)
{
bool bUp = j == iMaxI;
for (int pr = 0; pr < 1024; pr++)
{
int iMask = (pr & (1 << j)) ? 0 : (pr | (1 << j));
if (pr == 0)
{
iMask = 0;
}
dp[bUp][iMask] += pre[1][pr];
}
}
memcpy(pre, dp, sizeof(dp));
}
}
m_iRet += pre[0][0] + pre[1][0];
}
int m_iRet = 0;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

这篇关于【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/711793

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include