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 <iostream>
#include <string>
#include <stack>
using namespace std;

void problem_1802()
{
	typedef string::size_type size_type;
	int N; cin >> N;
	stack<char> right;
	stack<char> 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<char> container_type;
set<container_type> 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<char> tmp_left = left;
		stack<char> 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<char> container_type;
set<container_type> conds_set[10];
int fail_counter = 0;
void f(container_type& left, container_type& middle, const char pushed, const char end_chr)
{
	set<container_type>& conds = conds_set[pushed-'0'];
	stack<char> tmp_left = left;
	stack<char> 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<char>類型,給它起了別名叫做container_type,然後一個index對應多個不同的合理情況,爲了去除相同的情況使用set數據結構,index從1-9,那麼使用一個數組來代表9個index的組合。

但是每次Submit的時候都重新計算太浪費時間,所以應該把結果放入到一個數組中,輸入一個index和它對應的情況就查這個數組看看有沒有,這樣就只有查找,沒有重新計算了:

 

#include <fstream>

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<container_type> conds = conds_set[i];
		for (set<container_type>::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<container_type>::iterator itr2 = itr;
			itr2++;
			fs << "}";
			if (itr2 != conds.end())
			{//
				fs << ", ";
			}
			fs << endl;
		}
		fs << "}, " << endl;
	}

	fs.close();
}


 

走了。

 

 

 

 

 

 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章