Problem 1802 —— 火车调度

       最近在ecnu的ACM上看到一道题:http://acm.cs.ecnu.edu.cn/problem.php?problemid=1802是数据结构中关于火车调度的算法(已经忘了这道题了。。)。刚开始没有在网上查资料,自己在那瞎琢磨,想着通过检验字符的相互顺序来判断一个情况是否能出现,但总是感觉不对头,应该还有简单的方法。后来在网上查了资料,发现是用两个栈来模拟火车的进出。为了使讲述方便,新增一个栈,就是火车出站时使用的栈。这样就有三个栈:火车进站前进入栈right,在站中时,是在栈middle,出栈时进入栈left。

       比如对于输入“4 4321”,那么right将初始化为1234(从左往右看),检查input的第一个元素是4,因为middle跟right的top都不是元素4,那么火车1进站,此时middle中是1,right中是234,如上比较,直到middle中是321(从上往下看),right中是4,这时发现right中有元素4,那么right栈弹出,进入栈left(此题可以不使用left),input的指针前移,检测到元素3,因为在middle中有,所以3出站,这样直到检查完input,发现没有问题,输出“yes”。

       分析失败的情况:“4 4312”,right为1234,middle为空,left为空,4、3的检测跳过,这时middle为2,1,right为空,input指针指向1,但是middle的top不是1,所以只能从right中进站,但是right为空,这时发生冲突,1的位置是不对的。

       根据以上的分析给出代码:

#include 
#include 
#include 
using namespace std;

void problem_1802()
{
	typedef string::size_type size_type;
	int N; cin >> N;
	stack right;
	stack middle;
	
	for (int n = 0; n < N; n++)
	{//
		while (!right.empty())
		{//
			right.pop();
		}
		char index; cin >> index;
		for (char c = index; c > '0'; c--)
		{//
			right.push(c);
		}

		//因为火车的序号是一位数,而且input中的元素是连续的,所以使用string存储input
		string str; cin >> str;
		string::size_type i;
		bool failed; //判断标志,使用goto就没这么麻烦了。
		for (i = 0; i != str.size(); i++)
		{//
			failed = false;
			char c = str[i];
			//从middle和right中查找栈顶元素
			//如果找到则放入到left栈中
			if (!right.empty() && c == right.top())
			{//从right栈中找
				right.pop();
			}
			else if (!middle.empty() && c == middle.top())
			{//从middle栈中找
				middle.pop();
			}
			else
			{//否则两个栈的栈顶都不是查找元素,需要从right中将c“刨出来”
				failed = true;
				while (!right.empty())
				{//
					char cc = right.top();
					if (cc == c)
					{//放入到left中
						failed = false;
						right.pop();
						break;
					}
					
					//如果cc != c,那么将cc挖出来,放入到middle中,使c能重见天日。
					middle.push(cc);
					right.pop();
				}
			}
			if (failed)
			{//
				break;
			}
		}
		if (i == str.size())
		{//
			cout << "yes" << endl;
		}
		else
		{
			cout << "no" << endl;
		}
	}
}


       但是提交的时候是TImeLimited Fail,觉得可能是因为每次都对input进行检查太浪费时间了,应该先把index为1-9的列车所有的排列情况都算出来,存入一个set中查找。所以问题变成列举出列车所有的出站情况。

       当从right出栈时,一辆火车有两种选择:先进站,然后出站,或者先进站,但是不出站;这对应两种操作,从right中直接pop,或者push进middle中。

       那么递归的操作应该是:

             left.push(pushed);

              f(left, middle, pushed+1);

              left.pop();

              middle.push(pushed);

              f(left, middle, pushed+1);

              middle.pop();

       可见递归结束的条件是当pushed为'9'时停止,这时right中已经没有元素了,但是left和middle中有,比如left中的元素可能是1234,middle中的元素为8765,这时需要模拟火车的进出站顺序了:

              首先9可以直接进入left,然后middle中的所有元素出栈并压入left,right为123498765

              但是8也可能先于9进入left,然后9进入left,middle中的所有元素765进入left123489765

              ........

       可见是将9在middle的栈中依次“插队”,每种插队对应一种情况。下面是代码

typedef stack container_type;
set conds;
int fail_counter = 0; //用来检测重复情况
//计算9辆火车的情况
void f(container_type& left, container_type& middle, const char pushed)
{
	if (pushed ==	'9')
	{//处理right栈中的最后一列火车
		//为了保持left和middle的不变,使用tmp_进行操作
		stack tmp_left = left;
		stack tmp_middle = middle;
		for (size_t i = 0; i <= middle.size(); i++)
		{//将'9'在middle的栈中依次“插队”

			//将middle中,pushed前的元素放入到left中。
			for (size_t j = 0; j < i; j++)
			{//j是pushed在middle的队列中的位置
				tmp_left.push(tmp_middle.top());
				tmp_middle.pop();
			}
			//然后将pushed放入到left中
			tmp_left.push(pushed);
			while (!tmp_middle.empty())
			{//最后将middle中,pushed后的放入到left中
				tmp_left.push(tmp_middle.top());
				tmp_middle.pop();
			}

			//注意顺序,我们需要给出像input中那样的顺序,所以把栈中的元素颠倒放入到conds中
			while (!tmp_left.empty())
			{//
				tmp_middle.push(tmp_left.top());
				tmp_left.pop();
			}
			if (!(conds.insert(tmp_middle).second))
			{//检测是否重复
				//while (!tmp_middle.empty())
				//{//
				//	cout << tmp_middle.top() << ", ";
				//	tmp_middle.pop();
				//}
				//cout << endl;
				fail_counter++;
			}
			//重置
			tmp_left = left;
			tmp_middle = middle;
		}
	}
	
	left.push(pushed);
	f(left, middle, pushed+1);
	left.pop();
	middle.push(pushed);
	f(left, middle, pushed+1);
	middle.pop();
}

但是得到了9的出车情况,还有8的情况,7的情况……,好办,使用一个for循环就够了,将递归退出的if语句依次改为12345678就好了。

但是仔细研究发现,在得到9的出车情况时,其实已经计算了其它车的出车情况,比如,在puehed为5的情况中,left和middle中的所有元素都是不超过5的,也就是它们中是1234的各种合理组合,所以我们可以在计算9时计算12345678的合理情况:

typedef stack container_type;
set conds_set[10];
int fail_counter = 0;
void f(container_type& left, container_type& middle, const char pushed, const char end_chr)
{
	set& conds = conds_set[pushed-'0'];
	stack tmp_left = left;
	stack tmp_middle = middle;
	for (size_t i = 0; i <= middle.size(); i++)
	{//
		//将middle中,pushed前的元素放入到left中。
		for (size_t j = 0; j < i; j++)
		{//j是pushed在middle的队列中的位置
			tmp_left.push(tmp_middle.top());
			tmp_middle.pop();
		}
		//然后将pushed放入到left中
		tmp_left.push(pushed);
		//最后将middle中,pushed后的放入到left中
		while (!tmp_middle.empty())
		{//
			tmp_left.push(tmp_middle.top());
			tmp_middle.pop();
		}

		//注意顺序,我们需要给出像input中那样的顺序,所以把栈中的元素颠倒放入到conds中
		while (!tmp_left.empty())
		{//
			tmp_middle.push(tmp_left.top());
			tmp_left.pop();
		}

		if (!(conds.insert(tmp_middle).second))
		{//检测是否重复
			//while (!tmp_middle.empty())
			//{//
			//	cout << tmp_middle.top() << ", ";
			//	tmp_middle.pop();
			//}
			//cout << endl;
			fail_counter++;
		}
		tmp_left = left;
		tmp_middle = middle;
	}

	if (pushed ==	end_chr)
	{//
		return;
	}

	left.push(pushed);
	f(left, middle, pushed+1, end_chr);
	left.pop();
	middle.push(pushed);
	f(left, middle, pushed+1, end_chr);
	middle.pop();
}


数据结构的情况是,存储一种合理情况的是stack类型,给它起了别名叫做container_type,然后一个index对应多个不同的合理情况,为了去除相同的情况使用set数据结构,index从1-9,那么使用一个数组来代表9个index的组合。

但是每次Submit的时候都重新计算太浪费时间,所以应该把结果放入到一个数组中,输入一个index和它对应的情况就查这个数组看看有没有,这样就只有查找,没有重新计算了:

 

#include 

void to_file()
{
	fstream fs;
	fs.open("C:/1.txt", ios_base::out | ios::trunc);
	if (!fs)
	{//
		cout << "can't open!" << endl;
	}

	container_type left;
	container_type middle;
	f(left, middle, '1', '9');
	for (size_t i = 1; i < 10; i++)
	{//
		fs << "{" << endl;
		set conds = conds_set[i];
		for (set::iterator itr = conds.begin(); itr != conds.end(); ++itr)
		{//
			fs << "\t{";
			container_type& c = *itr;
			while (!c.empty())
			{//
				fs << c.top();
				if (c.size() != 1)
				{//
					fs << ", ";
				}
				c.pop();
			}
			set::iterator itr2 = itr;
			itr2++;
			fs << "}";
			if (itr2 != conds.end())
			{//
				fs << ", ";
			}
			fs << endl;
		}
		fs << "}, " << endl;
	}

	fs.close();
}


 

走了。

 

 

 

 

 

 

 


 

你可能感兴趣的:(火车调度,C++)