Рекомендовано автором
[Динамическое программирование] Алгоритм C++ 312 Poke the Balloon
Базовые знания, рассмотренные в этой статье.
динамическое программирование цифрового дп
LeetCode:233Количество цифр 1
Учитывая целое число n, подсчитайте количество вхождений числа 1 во все неотрицательные целые числа, меньшие или равные n.
Пример 1:
Входные данные: n = 13
Выходные данные: 6
Пример 2:
Входные данные: n = 0
Выходные данные: 0
Подсказка:
0 <= n <= 10 9
Класс инкапсуляции цифрового DP
Этот вопрос относительно прост, в основном речь идет о классах инкапсуляции. m_vPre записывает все состояния предыдущего бита.Когда программа завершается, записываются все состояния последнего бита.
m_vPre — двумерный вектор с одномерной длиной 4, представляющий четыре граничных состояния соответственно. Индекс 0 записывает неверхнюю и нижнюю границы, индекс 1 записывает нижнюю границу, индекс 2 записывает верхнюю границу и индекс 3 записывает одновременные верхнюю и нижнюю границы. Двумерная длина определяется параметром конструктора iResutlCount. Класс ResultType записывает состояние.
ОН | тип элемента перечисления |
МИНЫ | минимальное значение элемента |
МаксЭле | максимальное значение элемента |
pLower | Пустота |
pHвысокий | Верхняя граница |
напиток | Длина верхней и нижней границ |
При использовании OnEnumFirstBit и OnEnumOtherBit OnEnumFirstBit будет перечислять первый элемент и граничное состояние без дублирования или пропуска.
OnEnumOtherBit эта функция будет последовательно перечислять элементы и граничные состояния других битов без дублирования или пропуска.
Будут перечислены только состояния внутри диапазона, незаконные состояния перечисляться не будут.
pre и dp — состояния, соответствующие граничным состояниям, поэтому они являются одномерными векторами.
Примечание . Один и тот же элемент и одно и то же состояние могут быть перечислены дважды, что не приведет к двойному вычислению. Поскольку это двухуровневое перечисление, первый уровень перечисления: граничное состояние предыдущего элемента; второй уровень перечисления: текущий элемент.
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;
};
Код для этого вопроса
основной код
//pair<int, int> first记录在范围内符合要求的子串数量 second 记录1的数量
class CCharLowerUper : public CLowUperr<char, pair<int, int>, '0', '9'>
{
public:
using CLowUperr<char, pair<int, int>, '0', '9'>::CLowUperr;
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;
std::cout << " index:" << index << " " << cur << std::endl;
}
return ret;
}
protected:
virtual void OnEnumFirstBit(vector<pair<int, int>>& vPre, const char curValue)
{
const int index = curValue - '0';
vPre[index].first =1 ;
vPre[index].second = 1 == index;
}
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 < 10; i++)
{
dp[index].first += vPre[i].first;
dp[index].second += vPre[i].second;
if (1 == index)
{
dp[index].second += vPre[i].first;
}
}
}
};
class Solution {
public:
int countDigitOne(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(10);
lu.Init(("1" + string(i - 1, '0')).c_str(),string(i,'9').c_str(),i);
iRet += lu.Total(0, 9);
}
CCharLowerUper lu(10);
lu.Init(("1" + string(len - 1, '0')).c_str(), strN.c_str(), len);
iRet += lu.Total(0, 9);
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()
{
vector<int> nums;
{
Solution sln;
int n = 30;
auto res = sln.countDigitOne(n);
Assert(13, res);
}
{
Solution sln;
int n=13;
auto res = sln.countDigitOne(n);
Assert(6, res);
}
{
Solution sln;
int n = 0;
auto res = sln.countDigitOne(n);
Assert(0, res);
}
}
Выпуск за январь 2023 г.
решение класса { public: int countDigitOne (int n) { int iNum = 0; интервал iMul = 1; for (int i = 0; i < 9; i++) { iNum += n / (iMul * 10) *iMul; int tmp = n % (iMul * 10); if (tmp >= iMul) { if (tmp >= iMul * 2) { tmp = iMul * 2 - 1; } iNum += tmp - (iMul - 1); } iMul *= 10; } if (1000 * 1000 * 1000 == n) { iNum++; } Вернуть iNumber; } };
август 2023 г.
class CTest : public CLowUperr<char,int,'0','9'>
{ public: CTest():CLowUperr(10) { } // Наследование виртуального void OnDo(vector<vector>& dp, int preStatus, через CLowUperr , int curStatus, int cur) override { dp[curStatus][cur] += m_vPreCan[preStatus]; m_vCurOneNum[curStatus] += m_vPreOneNum[preStatus]; } virtual void OnInitDP(vector<vector>& dp) { for (int я = 0; я < 4; я++) { m_vPreCan[i] = std::accumulate(MACRO_BEGIN_END(m_vPre[i]), 0); m_vPreOneNum[i] = m_vCurOneNum[i] + m_vPre[i][1]; } memset(m_vCurOneNum, 0, sizeof(m_vCurOneNum)); } int Total() { int iRet = 0; for (int i = 0; i < 4; i++) { iRet += m_vCurOneNum[i] + m_vPre[i] [ 1]; } return iRet; } int m_vPreCan[4] = { 0 }; // Возможность первых четырёх состояний int m_vPreOneNum[4] = { 0 }; // Количество первых четырёх состояний 1 int m_vCurOneNum[4] ] = { 0 };//Количество первых четырёх состояний 1 };
решение класса { общественность: int countDigitOne(int n) { строка str = std::to_string(n); интервал Лен = 1; for (; len < str.length(); len++) { Do(“1” + string(len - 1, '0'), string(len, '9')); } Do(“1” + string(len - 1, '0'), str); вернуть m_iRet; } void Do (строка s1, строка s2) { тест CTest; test.Init(s1.data(), s2.data(), s1.length()); m_iRet += test.Total(); } INT m_iRet; };
дальнейшее чтение
видеокурс
Эффективное обучение: четкие цели, своевременная обратная связь и зона растяжения (соответствующая сложность).Сначала вы можете изучить простые курсы.Пожалуйста, зайдите в Академию CSDN и послушайте объяснения преподавателя Байинь (то есть меня).
https://edu.csdn.net/course/detail/38771
как быстро ты хочешь
Битва быстро развернулась. Чтобы разделить беспокойство начальника, пройдите обучение по C#, обучение по C++ и другие курсы
https://edu.csdn.net/lecturer/6176
Связанный
скачать
Для получения более продвинутых алгоритмов обучения загрузите документальную версию «Полной книги алгоритмов Си Цюэ»
https://download.csdn.net/download/he_zhidan/88348653
Что я хочу сказать всем |
---|
Хорошее пожелание радоваться, когда слышишь о недостатках, рано находить проблемы, вовремя их исправлять и экономить деньги для начальника. |
Зимози сказал: Ничто не имеет конца или начала, и нет дела и множества начинаний. Мы часто говорим: профессиональные люди делают профессиональные вещи. |
Если программа представляет собой единый процесс, то алгоритм является его ключевым моментом. |
тестовая среда
Операционная система: win7 Среда разработки: VS2019 C++17
или операционная система: win10 Среда разработки: VS2022 **C+
+17**
Если не указано иное, этот алгоритм реализован на **C++**.