1. 程式人生 > 其它 >Servlet+JSP+EL+JSTL+監聽器+過濾器總結

Servlet+JSP+EL+JSTL+監聽器+過濾器總結

目錄

HYSBZ - 1019漢諾塔

HDU 1207 漢諾塔II(四柱漢諾塔)

HDU 2064 漢諾塔III

HDU 2077 漢諾塔IV

HDU 1995 漢諾塔V

HDU 1996 漢諾塔VI

HDU 1997 漢諾塔VII

HDU 2184 漢諾塔VIII

HDU 2175 漢諾塔IX

HDU 2511 漢諾塔X


HYSBZ - 1019漢諾塔

題目:

 漢諾塔由三根柱子(分別用A B C表示)和n個大小互不相同的空心盤子組成。一開始n個盤子都摞在柱子A上,
大的在下面,小的在上面,形成了一個塔狀的錐形體。

 對漢諾塔的一次合法的操作是指:從一根柱子的最上層拿一個盤子放到另一根柱子的最上層,同時要保證被移

動的盤子一定放在比它更大的盤子上面(如果移動到空柱子上就不需要滿足這個要求)。我們可以用兩個字母來描
述一次操作:第一個字母代表起始柱子,第二個字母代表目標柱子。例如,AB就是把柱子A最上面的那個盤子移到
柱子B。漢諾塔的遊戲目標是將所有的盤子從柱子A移動到柱子B或柱子C上面。有一種非常簡潔而經典的策略可以幫
助我們完成這個遊戲。首先,在任何操作執行之前,我們以任意的次序為六種操作(AB、AC、BA、BC、CA和CB)
賦予不同的優先順序,然後,我們總是選擇符合以下兩個條件的操作來移動盤子,直到所有的盤子都從柱子A移動到
另一根柱子:(1)這種操作是所有合法操作中優先順序最高的;(2)這種操作所要移動的盤子不是上一次操作所移
動的那個盤子。可以證明,上述策略一定能完成漢諾塔遊戲。現在你的任務就是假設給定了每種操作的優先順序,計
算按照上述策略操作漢諾塔移動所需要的步驟數。

Input

  輸入有兩行。第一行為一個整數n(1≤n≤30),代表盤子的個數。第二行是一串大寫的ABC字元,代表六種操
作的優先順序,靠前的操作具有較高的優先順序。每種操作都由一個空格隔開。

Output

  只需輸出一個數,這個數表示移動的次數。我們保證答案不會超過10的18次方。

Sample Input
3
AB BC CA BA CB AC

Sample Output
7

根據最小的盤子的移動方式分類,其實本質上也就只有3種情況

其中一種情況是最普通的漢諾塔的情況,根據遞推式可以算出結果是2^n-1

另外2種情況也是差不多,結果分別是3^(n-1)和3^(n-1)*2-1

程式碼:

#include<iostream>
#include<string>
#define ll long long
using namespace std;
 
int pri[6];//AB BC CA BA AC CB 的優先順序
 
int id(char e, char f)
{
	if (e == 'A')if (f == 'B')return 0; else return 4;
	if (e == 'B')if (f == 'C')return 1; else return 3;
	if (f == 'A')return 2; else return 5;
}
 
int getpri(string s)
{
	return pri[id(s[0], s[1])];
}
 
ll f1(int n)//3^(n-1)
{
	ll ans = 1;
	while (--n)ans *= 3;
	return ans;
}
ll f2(int n)//3^(n-1)*2-1
{
	return f1(n) * 2 - 1;
}
ll f3(int n)//2^n-1
{
	ll ans = 1;
	while (n--)ans *= 2;
	return ans-1;
}
ll ans(int n)
{
	if (getpri("AB") > getpri("AC"))
	{
		if (getpri("BA") > getpri("BC"))return f2(n);
		if (getpri("CA") > getpri("CB"))return f3(n);
		return f1(n);
	}
	if (getpri("CA") > getpri("CB"))return f2(n);
	if (getpri("BA") > getpri("BC"))return f3(n);
	return f1(n);
}
 
int main()
{
	int n;
	cin >> n;
	string s;
	for (int i = 0; i < 6; i++)
	{
		cin >> s;
		pri[id(s[0], s[1])] = 6-i;//記錄AB BC CA BA AC CB 的優先順序
	}
	cout << ans(n);
	return 0;
}

HDU 1207 漢諾塔II(四柱漢諾塔)

題目:

Description

經典的漢諾塔問題經常作為一個遞迴的經典例題存在。可能有人並不知道漢諾塔問題的典故。漢諾塔來源於印度傳說的一個故事,上帝創造世界時作了三根金剛石柱子,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。上帝命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一回只能移動一個圓盤。有預言說,這件事完成時宇宙會在一瞬間閃電式毀滅。也有人相信婆羅門至今仍在一刻不停地搬動著圓盤。恩,當然這個傳說並不可信,如今漢諾塔更多的是作為一個玩具存在。Gardon就收到了一個漢諾塔玩具作為生日禮物。
  Gardon是個怕麻煩的人(恩,就是愛偷懶的人),很顯然將64個圓盤逐一搬動直到所有的盤子都到達第三個柱子上很困難,所以Gardon決定作個小弊,他又找來了一根一模一樣的柱子,通過這個柱子來更快的把所有的盤子移到第三個柱子上。下面的問題就是:當Gardon在一次遊戲中使用了N個盤子時,他需要多少次移動才能把他們都移到第三個柱子上?很顯然,在沒有第四個柱子時,問題的解是2^N-1,但現在有了這個柱子的幫助,又該是多少呢?

Input

包含多組資料,每個資料一行,是盤子的數目N(1<=N<=64)。

Output

對於每組資料,輸出一個數,到達目標需要的最少的移動數。

Sample Input

1
3
12

Sample Output

1
5
81

這個問題我有些想法,但我不確定。

後來我上網搜了一下,發現網上有篇部落格,裡面的操作方法和遞推式和我自己想的一模一樣。

據說這個叫Frame–Stewart演算法,還據說這個目前都沒有被證明正確性。

裡面有張圖片寫了遞推式(有一點點錯誤被我改過來了)

我自己求的遞推式也是這樣的,但是我不是很確定這是正確的。

程式碼:

#include<iostream>
using namespace std;


int main()
{
	/*long long list[65];
	long long a = 1;
	list[0] = 0;
	for (int i = 1; i <= 64; i++)
	{
		list[i] = 2 << 20;
		for (int j = 0; j < i; j++)
		{
			if (i - j > 20)continue;
			if (list[i] > list[j] * 2 + (a << (i - j)) - 1)list[i] = list[j] * 2 + (a << (i - j)) - 1;
		}
		cout << list[i] << ",";
	}*/
	int list[65] = { -1, 1, 3, 5, 9, 13, 17, 25, 33, 41, 49, 65, 81, 97, 113, 129, 161, 193, 225, 257, 289, 321, 385, 449, 513, 577, 641, 705, 769, 897, 1025, 1153, 1281, 1409, 1537, 1665, 1793, 2049, 2305, 2561, 2817, 3073, 3329, 3585, 3841, 4097, 4609, 5121, 5633, 6145, 6657, 7169, 7681, 8193, 8705, 9217, 10241, 11265, 12289, 13313, 14337, 15361, 16385, 17409, 18433 };
	int n;
	while (cin >> n)cout << list[n] << endl;
	return 0;
}

HDU 2064 漢諾塔III

題目:

Description

約19世紀末,在歐州的商店中出售一種智力玩具,在一塊銅板上有三根杆,最左邊的杆上自上而下、由小到大順序串著由64個圓盤構成的塔。目的是將最左邊杆上的盤全部移到右邊的杆上,條件是一次只能移動一個盤,且不允許大盤放在小盤的上面。
現在我們改變遊戲的玩法,不允許直接從最左(右)邊移到最右(左)邊(每次移動一定是移到中間杆或從中間移出),也不允許大盤放到小盤的上面。
Daisy已經做過原來的漢諾塔問題和漢諾塔II,但碰到這個問題時,她想了很久都不能解決,現在請你幫助她。現在有N個圓盤,她至少多少次移動才能把這些圓盤從最左邊移到最右邊?

Input

包含多組資料,每次輸入一個N值(1<=N=35)。

Output

對於每組資料,輸出移動最小的次數。

Sample Input

1
3
12

Sample Output

2
26
531440

這個題目的遞推式比較簡單,f(n)=3f(n-1)+2

可以直接求出通項公式,f(n)=3^n-1,也可以用陣列記錄所有的答案。

程式碼:

#include<iostream>
using namespace std;

int main()
{
	/*long long a = 0;
	for (int i = 1; i <= 35; i++)
	{
		a = a * 3 + 2;
		cout << a << ",";
	}*/
	long long l[35] = { 2, 8, 26, 80, 242, 728, 2186, 6560, 19682, 59048, 177146, 531440, 1594322, 4782968, 14348906, 43046720, 129140162, 387420488, 1162261466, 3486784400, 10460353202, 31381059608, 94143178826, 282429536480, 847288609442, 2541865828328, 7625597484986, 22876792454960, 68630377364882, 205891132094648, 617673396283946, 1853020188851840, 5559060566555522, 16677181699666568, 50031545098999706 };
	int n;
	while (cin >> n)cout << l[n - 1] << endl;
	return 0;
}

HDU 2077 漢諾塔IV

題目:

Description

還記得漢諾塔III嗎?他的規則是這樣的:不允許直接從最左(右)邊移到最右(左)邊(每次移動一定是移到中間杆或從中間移出),也不允許大盤放到小盤的上面。xhd在想如果我們允許最大的盤子放到最上面會怎麼樣呢?(只允許最大的放在最上面)當然最後需要的結果是盤子從小到大排在最右邊。

Input

輸入資料的第一行是一個數據T,表示有T組資料。
每組資料有一個正整數n(1 <= n <= 20),表示有n個盤子。

Output

對於每組輸入資料,最少需要的擺放次數。

Sample Input

2
1
10

Sample Output

2
19684

這個題目就稍微有些技巧了。

首先考慮,在漢諾塔III中,把n個盤子全部移到中間需要多少步。

其實如果仔細的思考的話,可以發現,在漢諾塔III中,要把n個盤子全部移到右邊,其實就是分2大步。

第一步,全部移到中間,第二步,全部移到右邊。

而且顯然他們的步數相等,所以都是f(n)=3^n-1的一半。

回到本題,整個過程分為3個部分。

一,把n-1個盤子移到中間,(3^(n-1)-1)/2步

二,把最大的盤子移到右邊,2步。

三,把n-1個盤子移到右邊,(3^(n-1)-1)/2步

一共3^(n-1)+1步。

程式碼:

#include<iostream>
using namespace std;

int main()
{
	/*long long a = 2;
	for (int i = 1; i <= 20; i++)
	{
		cout << a << ",";
		a = a * 3 - 2;
	}*/
	int l[20] = { 2, 4, 10, 28, 82, 244, 730, 2188, 6562, 19684, 59050, 177148, 531442, 1594324, 4782970, 14348908, 43046722, 129140164, 387420490, 1162261468 };
	int t, n;
	cin >> t;
	while (t--)
	{
		cin >> n;
		cout << l[n - 1] << endl;
	}
	return 0;
}

HDU 1995 漢諾塔V

題目:

Description

用1,2,...,n表示n個盤子,稱為1號盤,2號盤,...。號數大盤子就大。經典的漢諾塔問
題經常作為一個遞迴的經典例題存在。可能有人並不知道漢諾塔問題的典故。漢諾塔來源於
印度傳說的一個故事,上帝創造世界時作了三根金剛石柱子,在一根柱子上從下往上按大小
順序摞著64片黃金圓盤。上帝命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱
子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一回只能移動一個圓盤。我們
知道最少需要移動2^64-1次.在移動過程中發現,有的圓盤移動次數多,有的少 。 告之盤
子總數和盤號,計算該盤子的移動次數.

Input

包含多組資料,首先輸入T,表示有T組資料.每個資料一行,是盤子的數目N(1<=N<=60)和盤
號k(1<=k<=N)。

Output

對於每組資料,輸出一個數,到達目標時k號盤需要的最少移動數。

Sample Input

2
60 1
3 1

Sample Output

576460752303423488
4

首先,問題的答案肯定是關於n-k的一個函式。

於是接下來我們只需要考慮k=1的情況。

規律實在太明顯了,我就不扯什麼原理了。

答案是2^(n-k)

程式碼:

#include<iostream>
using namespace std;

int main()
{
	int t, n, k;
	long long a = 1;
	cin >> t;
	while (t--)
	{
		cin >> n >> k;
		cout << (a << (n - k)) << endl;
	}
	return 0;
}

注意,1<<(n-k)會溢位,所以需要long long的常數

HDU 1996 漢諾塔VI

題目:

Description

n個盤子的漢諾塔問題的最少移動次數是2^n-1,即在移動過程中會產生2^n個系列。由於
發生錯移產生的系列就增加了,這種錯誤是放錯了柱子,並不會把大盤放到小盤上,即各柱
子從下往上的大小仍保持如下關係 :
n=m+p+q
a1>a2>...>am
b1>b2>...>bp
c1>c2>...>cq
計算所有會產生的系列總數.

Input

包含多組資料,首先輸入T,表示有T組資料.每個資料一行,是盤子的數
目N<30.

Output

對於每組資料,輸出移動過程中所有會產生的系列總數。

Sample Input

3
1
3 
29

Sample Output

3
27
68630377364883

程式碼:

#include<iostream>
using namespace std;

int main()
{
	/*long long a = 3;
	for (int i = 1; i < 30; i++)
	{
		cout << a << ",";
		a = a * 3;
	}*/
	long long l[29] = { 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049, 177147, 531441, 1594323, 4782969, 14348907, 43046721, 129140163, 387420489, 1162261467, 3486784401, 10460353203, 31381059609, 94143178827, 282429536481, 847288609443, 2541865828329, 7625597484987, 22876792454961, 68630377364883 };
	int t, n;
	cin >> t;
	while (t--)
	{
		cin >> n;
		cout << l[n - 1] << endl;
	}
	return 0;
}

HDU 1997 漢諾塔VII

題目:

Description

n個盤子的漢諾塔問題的最少移動次數是2^n-1,即在移動過程中會產生2^n個系列。由於發生錯移產生的系列就增加了,這種錯誤是放錯了柱子,並不會把大盤放到小盤上,即各柱子從下往上的大小仍保持如下關係 :
n=m+p+q
a1>a2>...>am
b1>b2>...>bp
c1>c2>...>cq
ai是A柱上的盤的盤號系列,bi是B柱上的盤的盤號系列, ci是C柱上的盤的盤號系列,最初目標是將A柱上的n個盤子移到C盤. 給出1個系列,判斷它是否是在正確的移動中產生的系列.
例1:n=3
3
2
1
是正確的
例2:n=3
3
1
2
是不正確的。
注:對於例2如果目標是將A柱上的n個盤子移到B盤. 則是正確的.

Input

包含多組資料,首先輸入T,表示有T組資料.每組資料4行,第1行N是盤子的數目N<=64.
後3行如下
m a1 a2 ...am
p b1 b2 ...bp
q c1 c2 ...cq
N=m+p+q,0<=m<=N,0<=p<=N,0<=q<=N,

Output

對於每組資料,判斷它是否是在正確的移動中產生的系列.正確輸出true,否則false

Sample Input

6
3
1 3
1 2
1 1
3
1 3
1 1
1 2
6
3 6 5 4
1 1
2 3 2
6
3 6 5 4
2 3 2
1 1
3
1 3
1 2
1 1
20
2 20 17
2 19 18
16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

Sample Output

true
false
false
false
true
true

因為我是先做了漢諾塔VIII然後才做的漢諾塔VII(本題),所以是直接用的漢諾塔VIII的結論做的。

其實就是求是否存在這樣的一個m,使得m次移動之後的狀態就是題目所給的狀態。

如果我輸出的是true,其實這個時候的m的值表示的剛好就是,m次移動之後的狀態就剛好滿足。

那麼m到底怎麼求呢?顯然和搜尋無關。

其實就是n個方程,漢諾塔VIII裡面是m已知,那麼n個方程依次可以求出每個盤子在哪。

反過來,就是關於m的一元n次方程組,問是否有解。

如果把m看成x1+2 * x2+4 * x3+8 * x4。。。其中xi是0或者1,即m的每一位,那麼該方程就是n元n次方程組。

而且本題不需要求解方程組的方法,只需要依次求出各個xi即可(或者判定為無解)

相當於係數矩陣為下三角矩陣的n元n次方程組,只要每次都把前面已經求出來的解代入,即可求出1解,直到全部求出。

程式碼:

#include<iostream>
using namespace std;

int list[65];

int main()
{
	int t, n, l, a, f;
	long long m;
	cin >> t;
	while (t--)
	{
		cin >> n;
		for (int i = 0; i < 3; i++)
		{
			cin >> l;
			while (l--)
			{
				cin >> a;
				list[a] = i;
			}
		}
		m = 0;
		bool flag = false;
		for (int i = n; i>0; i--)
		{
			m *= 2;
			f = ((i % 2) * 2 - 1)*((n % 2) * 2 - 1)*list[i];
			if ((m - f) % 3 == 0)continue;
			else if (((m - f) % 3 + 2) % 3 == 0)m += 1;
			else
			{
				flag = true;
				break;
			}
		}
		if (flag)cout << "false" << endl;
		else cout << "true" << endl;
	}
	return 0;
}

HDU 2184 漢諾塔VIII

題目:

Description

1,2,...,n表示n個盤子.數字大盤子就大.n個盤子放在第1根柱子上.大盤不能放在小盤上.在第1根柱子上的盤子是a[1],a[2],...,a[n]. a[1]=n,a[2]=n-1,...,a[n]=1.即a[1]是最下面的盤子.把n個盤子移動到第3根柱子.每次只能移動1個盤子,且大盤不能放在小盤上.問移動m次後的狀況.

Input

第1行是整數T,表示有T組資料,下面有T行
每行2個整數n (1 ≤ n ≤ 63) ,m≤ 2^n-1

Output

輸出移動m次後的狀況.每組輸出3行,第i行第1個數是第i根柱子上的盤子數,然後是盤子的號數.

Sample Input

3
3 2
4 5
39 183251937942

Sample Output

1 3
1 2
1 1
2 4 1
1 3
1 2
13 39 36 33 30 27 24 21 18 15 12 9 4 1
12 38 35 32 29 26 23 20 17 14 11 8 5
14 37 34 31 28 25 22 19 16 13 10 7 6 3 2

程式碼:

//我驚奇的發現其實我的輸出結果,連n=4,m=5的時候都不對
//當n為偶數的時候,後面2行我輸出的是反的
#include<iostream>
#include<stack>
using namespace std;
 
stack<int>s[3];
 
int main()
{
	int t;
	cin >> t;
	long long n, g, p, f;
	long long m;
	long long a = 1;
	while (t--)
	{
		cin >> n >> m;
		for (int i = 0; i < 3; i++)while (!s[i].empty())s[i].pop();
		for (int i = 1; i <= n; i++)
		{
			g = (m >> (i + 1)) % 3;
			f = (i % 2) * 2 - 1;
		p = (f*(g - ((m&(a << i))>0) - ((m&(a << (i - 1)))>0) + 3) % 3 + 3) % 3;
			s[p].push(i);
		}
		for (int i = 0; i < 3; i++)
		{
			cout << s[i].size();
			while (!s[i].empty())
			{
				cout << " " << s[i].top();
				s[i].pop();
			}
			cout << endl;
		}
	}
	return 0;
}

嚇得我趕緊重寫重提交,又AC了一次。順便把m改成了每次都除2。

程式碼:

#include<iostream>
#include<stack>
using namespace std;
 
stack<int>s[3];
 
int main()
{
	int t;
	cin >> t;
	long long n, p, f;
	long long m;
	while (t--)
	{
		cin >> n >> m;
		for (int i = 0; i < 3; i++)while (!s[i].empty())s[i].pop();
		for (int i = 1; i <= n; i++)
		{
			f = ((i % 2) * 2 - 1)*((n % 2) * 2 - 1);	//移動的方向
			p = (f*(m + m % 2) % 3 + 3) % 3;	//移動的距離
			m /= 2;
			s[p].push(i);
		}
		for (int i = 0; i < 3; i++)
		{
			cout << s[i].size();
			while (!s[i].empty())
			{
				cout << " " << s[i].top();
				s[i].pop();
			}
			cout << endl;
		}
	}
	return 0;
}

原理就不解釋了,可以把m化成二進位制觀察規律。(關於m的運算可以看看上面漢諾塔VII的講解,還有下面的題目)

HDU 2175 漢諾塔IX

題目:

Description

1,2,...,n表示n個盤子.數字大盤子就大.n個盤子放在第1根柱子上.大盤不能放在小盤上.
在第1根柱子上的盤子是a[1],a[2],...,a[n]. a[1]=n,a[2]=n-1,...,a[n]=1.即a[1]是最下
面的盤子.把n個盤子移動到第3根柱子.每次只能移動1個盤子,且大盤不能放在小盤上.
問第m次移動的是那一個盤子.

Input

每行2個整數n (1 ≤ n ≤ 63) ,m≤ 2^n-1.n=m=0退出

Output

輸出第m次移動的盤子的號數.

Sample Input

63 1
63 2
0 0

Sample Output

1
2

這個題目的規律描述起來比較簡單。假設m的最後一個1在從右往左第i位,那麼第i次操作就是移動第i個盤子。

有個約瑟夫問題和這個差不多點選開啟我的部落格

關於約瑟夫問題和漢諾塔問題的緊密聯絡,別人都做了很多研究,我就不扯了,反正都是二進位制就對了。

其實上面的漢諾塔VIII是用到了這個結論的,不過我沒有明說出來。

程式碼:

#include<iostream>
using namespace std;


int main()
{
	int n;
	long long m;
	while (cin >> n >> m)
	{
		if (n == 0)break;
		int i = 1;
		while (m % 2 == 0)
		{
			i++;
			m /= 2;
		}
		cout << i << endl;
	}
	return 0;
}

HDU 2511 漢諾塔X

題目:

Description

1,2,...,n表示n個盤子.數字大盤子就大.n個盤子放在第1根柱子上.大盤不能放在小盤上.在第1根柱子上的盤子是a[1],a[2],...,a[n]. a[1]=n,a[2]=n-1,...,a[n]=1.即a[1]是最下面的盤子.把n個盤子移動到第3根柱子.每次只能移動1個盤子,且大盤不能放在小盤上.問第m次移動的是哪一個盤子,從哪根柱子移到哪根柱子.例如:n=3,m=2. 回答是 :2 1 2,即移動的是2號盤,從第1根柱子移動到第2根柱子 。

Input

第1行是整數T,表示有T組資料,下面有T行,每行2個整數n (1 ≤ n ≤ 63) ,m≤ 2^n-1

Output

輸出第m次移動的盤子號數和柱子的號數.

Sample Input

4
3 2
4 5
39 183251937942
63 3074457345618258570

Sample Output

2 1 2
1 3 1
2 2 3
2 2 3

這個題目,真的是。。。把上面2個題目的程式碼雜交,得到的就是正確的程式碼

程式碼:

#include<iostream>
using namespace std;


int main()
{
	int t, n;
	long long m;
	cin >> t;
	while (t--)
	{
		cin >> n >> m;
		if (n == 0)break;
		int i = 1;
		while (m % 2 == 0)
		{
			i++;
			m /= 2;
		}
		cout << i << " ";
		int f = ((i % 2) * 2 - 1)*((n % 2) * 2 - 1);	//移動的方向
		cout << (f*((m - 1) + (m - 1) % 2) % 3 + 3) % 3 + 1 << " " << (f*(m + m % 2) % 3 + 3) % 3 + 1 << endl;		
	}
	return 0;
}