hdu 1997汉诺塔
题目分析:
转自:http://lcc3536.blog.163.com/blog/static/132469917201132283640123/
1) 最初我们要判断一下是不是已经完全放好了,这样就不用考虑是不是最优化了, 因为都已经放好了,肯定是最合法的! 或者说全部在 A 上,这是还没开始动作的一个状态,所以也是合法的!
2) 否则我们 要对每次状态的最大的那个进行判断,因为我们知道,汉诺塔最大的那个不可能停在 B 上,(假设 最初的时候都在 A 上,要移到 C 上去!),只可能在 A 或者 C 上面!如果是放在 B 上面,停止判断,直接断定他非法~这样我们得出了第二个判段依据!
3) 如果最大的在 A 上面,我可以想到的是,他还没有放在 C 上,此时这个状态的上面一系列状态都是想把其他的放在 B 上,然后就可以把 A 放到 C 上了,所以我们在这里做出更新,因为他上面一系列动作都是想让 A 上面的除最大的外都放到 B 上面去,所以,我们这个时候往上考虑,对 N-1 进行 判断!这个时候动作的方向是 A->B 所以为了统一操作,我们得把 B 跟C互换!
4) 如果最大的在 C 上面,这时候倒数第二大的不是放在 B 上就是 C 上,我们要把要把倒数第二大的以及其他的放在 C 上,这时候的动作方向是 B—>C ;所以把 A 跟 B 交换一下!
#include<iostream>
using namespace std;
int main()
{
int a=1,b=2,c=3;
int h[4][65],n[4],one[4],N;
int cas;
cin>>cas;
while(cas--)
{
int flag=0;
one[a]=one[b]=one[c]=1;
cin>>N;
cin>>n[a];
for(int i=one[a];i<=n[a];i++)
cin>>h[a][i];
cin>>n[b];
for(int i=one[b];i<=n[b];i++)
cin>>h[b][i];
cin>>n[c];
for(int i=one[c];i<=n[c];i++)
cin>>h[c][i];
while(1)
{
if(n[a]==N||n[c]==N)
{
flag=1;break;
}
if(n[b]>0&&h[b][one[b]]==N)
{
flag=0;break;
}
if(n[a]>0&&h[a][one[a]]==N)//交换b和c
{
N--;one[a]++;
n[a]--;
int t=b;
b=c;c=t;
continue;
}
if(n[c]>0&&h[c][one[c]]==N)//交换a和b
{
N--;one[c]++;
n[c]--;
int t=a;
a=b;b=t;
continue;
}
}
if(flag)cout<<"true"<<endl;
else cout<<"false"<<endl;
}
return 0;
}
题目分析:
汉诺塔是我很喜欢玩的一个游戏。
如果规则没这么变态,允许直接从1跨越到3,那n个盘最少需要2n - 1次。
这个公式可以递推得到,很容易的,你可以试一下,这里就不阐述了。
而这一题同样可以用递推得到。我们先来看看下面一组图,了解一下如何把n个盘从1搬到3。
我们设f(n)为把n个盘从1移到3所需要的步数,当然也等于从3移到1的步数。
看什么的图就知道,要想把第n个盘从1移到3,需要想把前n-1个从1移动3,再从3->1最后再1->3。
而第n个盘要从1->2->3经历2步。
∴f(n) = 3 × f(n-1) + 2;
f(1) = 2;
#include<iostream>
using namespace std;
__int64 f[36];
void init()
{
f[1]=2;
for(int i=2;i<=35;i++)
f[i]=3*f[i-1]+2;
}
int main()
{
int n;
init();
while(scanf("%d",&n)==1)
{
printf("%I64d\n",f[n]);
}
return 0;
}