1. 程式人生 > >UVa 679 例題6-6 小球下落(Dropping Balls)

UVa 679 例題6-6 小球下落(Dropping Balls)

題目大意:

有一顆滿二叉樹,每個節點是一個開關,初始全是關閉的,小球從頂點落下,小球每次經過開關就會把它的狀態置反,現在問第k個球下落到d層時經過的開關編號。

解題思路:

這道題一開始看的時候,感覺不是很複雜,看了題目就開始自己實現了。

 程式碼一一開始(為什麼說一開始,因為後面還有另一方法啊!)思路很直接,就是用一個2^m大小的陣列來模擬二叉樹,第i個位置的左右節點分別是i*2和i*2+1.然後模擬從1到n個小球的下落過程。最後就能順利獲得最後一個小球的下落位置了。(程式碼一

 (非重點,吐槽)是不是很簡單?昂?當時感覺真爽啊!寫完一個bug都沒啊!直接出正確結果啊!debug都不用啊!果然寫水題很有成就感啊!然後就準備提交,為了以防萬一我還在

Udebug上找了一組輸入測試了一下。通過!爽!然後就提交了,心裡默想不AC我直播吃屎。(劃掉,為什麼要劃掉呢,因為我沒說過啊,哈哈哈)然後,然後,然後就TLE了。(你在逗我吧,怎麼可能不AC,一定是OJ出毛病了,爛OJ,恩,絕對是這樣。)然後我就去看紫書上的程式碼,這麼思路完全和我的一樣啊,那為毛不能AC啊,吃屎吧!然後直到我開始翻下一頁,MMP,竟然還可以這樣,吃屎吧你。。。

 程式碼二另一種更優的思路是,當一個小球在某個結點時,可以直接根據該小球是第幾個落在該結點上的來判斷他的下落方向,如果是如果是奇數則應該落到他的左結點,如果是奇數則應該落到他的有節點。而且每個結點上小球落上去的平均次數是隨著層數減半的。根據這些原理就能夠直接求最後一個小球的下落路徑。從而減少一層迴圈的時間,也減少了一個表示二叉樹的超大陣列。

感悟:

 看來簡單的題也不一定簡單啊。(doge臉)

程式碼:

程式碼一:

#include<stdio.h>
#include<string.h>

using namespace std;

const int MAXN = 524288 + 100;

int tree[MAXN];

int main()
{
	//freopen("input.txt","r",stdin);
	//freopen("output.txt","w",stdout);
	int num,n, m;
	while(scanf("%d",&num) == 1 && num !=-1)
	{
		while (num--) 
		{
			scanf("%d %d",&n,&m);
			int ball;
			memset(tree, 0, sizeof(tree));
			for (int i = 0; i < m; i++)
			{
				ball = 1;
				for (int j = 1; j < n; j++)
				{
					int b = ball;
					if (tree[ball])
						ball = 2 * ball + 1;
					else
						ball = 2 * ball;
					tree[b] = !tree[b];
				}
			}
			printf("%d\n",ball);
		}
	}
	return 0;
}

程式碼二:

#include<iostream>

using namespace std;

int main()
{
	int num,n, m;
	while(cin >> num && num !=-1)
	{
		while (num--) 
		{
			cin >> n >> m;
			int ball = 1;
			for (int j = 1; j < n; j++)
			{
				if (m % 2)
				{
					ball = ball * 2;
					m = (m + 1) / 2;
				}
				else
				{
					ball = ball * 2 + 1;
					m = m / 2;
				}
			}
			cout << ball << endl;
		}
	}
	return 0;
}