[Цифровой dp] [Динамическое программирование] Алгоритм C++: 233. Число цифр 1

Рекомендовано автором

[Динамическое программирование] Алгоритм 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++**.

Guess you like

Origin blog.csdn.net/he_zhidan/article/details/135422239