利用广度优先或模拟解决米诺骨牌

本周推荐阅读

C++二分算法:得到子序列的最少操作次数

题目

n 张多米诺骨牌排成一行,将每张多米诺骨牌垂直竖立。在开始时,同时把一些多米诺骨牌向左或向右推。

每过一秒,倒向左边的多米诺骨牌会推动其左侧相邻的多米诺骨牌。同样地,倒向右边的多米诺骨牌也会推动竖立在其右侧的相邻多米诺骨牌。

如果一张垂直竖立的多米诺骨牌的两侧同时有多米诺骨牌倒下时,由于受力平衡, 该骨牌仍然保持不变。

就这个问题而言,我们会认为一张正在倒下的多米诺骨牌不会对其它正在倒下或已经倒下的多米诺骨牌施加额外的力。

给你一个字符串 dominoes 表示这一行多米诺骨牌的初始状态,其中:

dominoes[i] = 'L',表示第 i 张多米诺骨牌被推向左侧,

dominoes[i] = 'R',表示第 i 张多米诺骨牌被推向右侧,

dominoes[i] = '.',表示没有推动第 i 张多米诺骨牌。

返回表示最终状态的字符串。

示例 1:

输入:dominoes = "RR.L"

输出:"RR.L"

解释:第一张多米诺骨牌没有给第二张施加额外的力。

示例 2:

输入:dominoes = ".L.R...LR...L..."

输出:"LL.RR.LLRRLL..."

提示:

n == dominoes.length

1 <= n <= 105

dominoes[i] 为 'L'、'R' 或 '.'

![(https://img-blog.csdnimg.cn/7ddeab69c86f43ee9c3a704d603a85f4.png#pic_center)

初始想法

将LR放到栈中。

如果遇到两个L 则两个L之间的全部L ,出栈入栈
栈顶L,新遇到R 不处理,新元素入栈
如果遇到两个R 两个R之间全部R,出栈入栈
栈顶R 新遇到L RL之间离R近的R,离L近的L,相等的不边,出栈入栈

结束时,栈有如下几种情况

LR
L
R

分情况讨论过于复杂,放弃。

可行的方案

时间复杂度: O(n) ,两轮循环时间复杂度都是O(n)。

分析

向左倒 右边必须有牌向左倒,且不被向右倒的牌挡住
向右倒 左边必须有牌向右倒,且不被向左倒的牌挡住
左右倒 距离左倒的近,左倒;距离右倒的近,右倒;距离相等,直立

变量解释

vRight 距离最近的向右倒的牌的索引
iLeft 距离最近向左倒的牌的索引

核心代码

cpp 复制代码
class Solution {
public:
	string pushDominoes(string dominoes) {
		m_c = dominoes.length();
		vector<int> vRight(m_c, -1);
		{
			int iRight = -1;
			for (int i = 0; i < m_c; i++)
			{
				if ('R' == dominoes[i])
				{
					iRight = i;
				}
				if ('L' == dominoes[i])
				{
					iRight = -1;
				}
				vRight[i] = iRight;
			}
		}
		int iLeft = -1;
		string s(m_c, '.');
		for (int i = m_c - 1; i >= 0; i--)
		{
			if ('L' == dominoes[i])
			{
				iLeft = i;
			}
			if ('R' == dominoes[i])
			{
				iLeft = -1;
			}			
			if (iLeft >= 0)
			{
				if (vRight[i] >= 0)
				{
					int tmp = (iLeft - i) - (i - vRight[i]);
					if (tmp > 0)
					{
						s[i] = 'R';
					}
					else if (tmp < 0)
					{
						s[i] = 'L';
					}
				}
				else
				{
					s[i] = 'L';
				}
			}
			else if(vRight[i] >= 0)
			{
				s[i] = 'R';
			}
		}
		return s;
	}
	int m_c;
};

测试用例

cpp 复制代码
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()
{
	string dominoes;
	string res;
	{
		Solution slu;
		dominoes = "...";
		res = slu.pushDominoes(dominoes);
		Assert(res, string("..."));
	}
	{
		Solution slu;
		dominoes = ".L.";
		res = slu.pushDominoes(dominoes);
		Assert(res, string("LL."));
	}
	{
		Solution slu;
		dominoes = ".R.";
		res = slu.pushDominoes(dominoes);
		Assert(res, string(".RR"));
	}
	{
		Solution slu;
		dominoes = "R";
		res = slu.pushDominoes(dominoes);
		Assert(res, string("R"));
	}
	{
		Solution slu;
		dominoes = "L";
		res = slu.pushDominoes(dominoes);
		Assert(res, string("L"));
	}
	{
		Solution slu;
		dominoes = ".";
		res = slu.pushDominoes(dominoes);
		Assert(res, string("."));
	}
	{
		Solution slu;		
		dominoes = "RR.L";
		res = slu.pushDominoes(dominoes);
		Assert(res, string("RR.L"));
	}
	{
		Solution slu;
		dominoes = ".L.R...LR..L..";
		res = slu.pushDominoes(dominoes);
		Assert(res, string("LL.RR.LLRRLL.."));
	}
	
	//CConsole::Out(res);
}

深度优先搜索

分析

时间复杂度: O(n) ,每个元素最多出队入队一次。

变量解释

que 记录当前回合左到或右倒的元素索引
mIndexValue 键:当前回合受到力的牌,值:当前回合受到力。1,向左的力;0,没受到力或同时受到左右的力。-1,受到向右的力。

注意: 力值能让没倒的牌倒下,无法让倒下的牌转变方向。

cpp 复制代码
if ('.' != dominoes[it.first])
			{
				continue;
			}

代码

cpp 复制代码
class Solution {
public:
	string pushDominoes(string dominoes) {
		m_c = dominoes.length();
		std::queue<int> que;
		for (int i = 0; i < m_c; i++)
		{
			if ('.' != dominoes[i])
			{
				que.emplace(i);
			}
		}
		while (que.size())
		{
			unordered_map<int, int> mIndexValue;
			while (que.size())
			{
				const int inx = que.front();
				que.pop();
				if ('L' == dominoes[inx])
				{
					if (inx > 0)
					{
						mIndexValue[inx - 1]++;
					}
				}
				else 
				{
					if (inx + 1 < m_c)
					{
						mIndexValue[inx + 1]--;
					}
				}
			}
			for (const auto& it : mIndexValue)
			{
				if ('.' != dominoes[it.first])
				{
					continue;
				}
				if (1 == it.second)
				{
					dominoes[it.first] = 'L';
					que.emplace(it.first);
				}
				if (-1 == it.second)
				{
					dominoes[it.first] = 'R';
					que.emplace(it.first);
				}
			}
		}
		return dominoes;
	}
	int m_c;
};

模拟

分析

时间复杂度 😮(n)。

枚举所有连续的直立牌,根据两边的受力方向分情况讨论。为了简化问题:可以想象在最左边增加L,最右边增加R。

LL 全部左倒
LR 全部不边
RL 左边右倒,右边左倒
RR 全部右倒

代码

cpp 复制代码
class Solution {
public:
	string pushDominoes(string dominoes) {
		m_c = dominoes.length();
		int iPre = -1;
		for (int i = 0; i < m_c; i++)
		{
			if ('.' != dominoes[i])
			{
				Do(dominoes,iPre, i);
				iPre = i;
			}
		}	
		Do(dominoes, iPre, m_c);
		return dominoes;
	}
	void Do(string& s, int left, int right)
	{
		const char chL = (left < 0) ? 'L' : s[left];
		const char chR = (right < m_c) ? s[right] : 'R';
		if ('L' == chL)
		{
			if ('L' == chR)
			{
				for (int i = left + 1; i < right; i++)
				{
					s[i] = 'L';
				}
			}
			else
			{
				//什么都不干
			}
		}
		else
		{
			if ('L' == chR)
			{
				for (int i = 1; i <= (right - left - 1) / 2; i++)
				{
					s[left + i] = 'R';
					s[right - i] = 'L';
				}
			}
			else
			{
				for (int i = left + 1; i < right; i++)
				{
					s[i] = 'R';
				}
			}
		}
	}
	int m_c;
};

2022年12月旧版

class Solution {

public:

string pushDominoes(string dominoes) {

c = dominoes.length();

std::unordered_map<int, int> mPreIndexValue;

for (int i = 0; i < c; i++)

{

const char& ch = dominoes[i];

Test(mPreIndexValue,ch, i);

}

while (mPreIndexValue.size())

{

std::unordered_map<int, int> dp;

for (auto& it : mPreIndexValue)

{

if (0 == it.second)

{

continue;

}

if ('.' != dominoes[it.first])

{

continue;

}

dominoes[it.first] = (1 == it.second) ? 'L' : 'R';

Test(dp,dominoes[it.first], it.first);

}

dp.swap(mPreIndexValue);

}

return dominoes;

}

void Test(std::unordered_map<int, int>& m,const char& ch, int i)

{

if ('L' == ch)

{

if (i > 0 )

{

m[i - 1]++;

}

}

else if ('R' == ch)

{

if (i + 1 < c)

{

m[i + 1]--;

}

}

}

int c;

};

2023年11月旧版

class Solution {

public:

string pushDominoes(string dominoes) {

int n = dominoes.size();

vector vRight(n, -1);

{

int iR = -1;

for (int i = 0; i < n; i++)

{

if ('R' == dominoes[i])

{

iR = i;

}

else if ('L' == dominoes[i])

{

iR = -1;

}

vRight[i] = iR;

}

}

int iLeft = -1;

string s(n, '.');

for (int i = n - 1; i >= 0; i--)

{

if ('L' == dominoes[i])

{

iLeft = i;

}

else if ('R' == dominoes[i])

{

iLeft = -1;

}

if ((iLeft >= 0)&&(vRight[i] >= 0 ))

{

const int tmp = (iLeft - i) - (i - vRight[i]);

if (tmp > 0)

{

s[i] = 'R';

}

if (tmp < 0)

{

s[i] = 'L';

}

}

else if (iLeft >= 0)

{

s[i] = 'L';

}

else if (vRight[i] >= 0)

{

s[i] = 'R';

}

}

return s;

}

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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

相关推荐
熬夜学编程的小王16 分钟前
C++类与对象深度解析(一):从抽象到实践的全面入门指南
c++·git·算法
CV工程师小林18 分钟前
【算法】DFS 系列之 穷举/暴搜/深搜/回溯/剪枝(下篇)
数据结构·c++·算法·leetcode·深度优先·剪枝
Dylanioucn22 分钟前
【分布式微服务云原生】掌握 Redis Cluster架构解析、动态扩展原理以及哈希槽分片算法
算法·云原生·架构
繁依Fanyi31 分钟前
旅游心动盲盒:开启个性化旅行新体验
java·服务器·python·算法·eclipse·tomcat·旅游
罔闻_spider41 分钟前
爬虫prc技术----小红书爬取解决xs
爬虫·python·算法·机器学习·自然语言处理·中文分词
zh路西法1 小时前
基于opencv-C++dnn模块推理的yolov5 onnx模型
c++·图像处理·pytorch·opencv·yolo·dnn·yolov5
-指短琴长-1 小时前
BFS解决多源最短路问题_01矩阵_C++【含多源最短路问题介绍+dist数组介绍】
c++·矩阵·宽度优先
小码农<^_^>1 小时前
c++继承(下)
开发语言·c++
Themberfue1 小时前
基础算法之双指针--Java实现(下)--LeetCode题解:有效三角形的个数-查找总价格为目标值的两个商品-三数之和-四数之和
java·开发语言·学习·算法·leetcode·双指针
盒马盒马1 小时前
Redis:cpp.redis++通用接口
数据库·c++·redis