uva 10254 The Priest Mathematician

原题:
The ancient folklore behind the “Towers of Hanoi” puzzle invented by E. Lucas in 1883 is quite well known to us. One more recent legend tells us that the Brahmin monks from Benares never believed that the world could vanish at the moment they finished to transfer the 64 discs from the needle on which they were to one of the other needles, and they decided to finish the task as soon as possible.
这里写图片描述
One of the priests at the Benares temple (who loved the mathematics) assured their colleagues to
achieve the transfer in the afternoon (the rhythm they had thought was a disc-per-second) by using an additional needle. They couldn’t believe him, but he proposed them the following strategy:
• First move the topmost discs (say the top k discs) to one of the spare needles.
• Then use the standard three needles strategy to move the remaining n − k discs (for a general
case with n discs) to their destination.
• Finally, move the top k discs into their final destination using the four needles.
He calculated the value to k in order to minimize the number of movements and get a total of 18433
transfers, so they spent just 5 hours, 7 minutes and 13 seconds against the more than 500000 millions years without the additional needle (as they would have to do 2 64 − 1 disc transfers. Can you believe it?)
Try to follow the clever priest’s strategy and calculate the number of transfer using four needles but according with the fixed and immutable laws of Brahma, which require that the priest on duty must not move more than one disc at a time and that he must place this disc on a needle so that there is no smaller disc below it. Of course, the main goal is to calculate the k that minimize the number of transfers (even thought it is not know for sure that this is always the optimal number of movements).
Input
The input file contains several lines of input. Each line contains a single integer N, which is the number of disks to be transferred. Here 0 ≤ N ≤ 10000. Input is terminated by end of file.
Output
For each line of input produce one line of output which indicates the number of movements required to transfer the N disks to the final needle.
Sample Input
1
2
28
64
Sample Output
1
3
769
18433
中文:
(来自lucky 猫)
個在 Benares 神殿的的祭司(他喜歡數學)跟他的同事保證,如果用一個額外的柱子,就可以在下午完成搬運(他們認為每秒可以搬一個盤子)。他們不相信他,但是他向他們提出以下策略:
– 把最上面的 k 個盤子搬到備用的柱子。
– 然後用原本的三個柱子的策略去把剩下的 n-k 的盤子搬到他們的目標。
– 最後用四個柱子把最上面的 k 個盤子搬到目標。

他計算 k 的值使得移動的次數最少,結果是總共 18433 次,所以他們只要花 5 個小時 7 分 13 秒,比不用一個額外柱子的 5000 億年(他們要搬 2^64-1 次盤子,你相信嗎?)好多了。

試著照這個聰明的祭司的策略計算用四個柱子要搬幾次。根據 Brahma 固定不變的法律,一次只能移動一個盤子,而且不能放在比較小的盤子上面。當然,主要的目標是計算 使搬移次數(就算不確定這是不是最佳的移動次數)最少的 k。

Input
輸入檔包含很多行輸入,每行都有一個整數 N,表示要搬的盤子數量。這裡 0<=N<=10000。Input 以 end-of-file 結束。
Output
對每行輸入輸出一行把 N 個盤子搬到最後的柱子需要搬動幾次。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000;/*精度位数*/
/*(必选)类与基础功能定义,用法类似于unsigned(非负)*/
class bign
{
    friend istream& operator>>(istream&,bign&);/*输入运算符友元*/
    friend ostream& operator<<(ostream&,const bign&);/*输出运算符友元*/
    friend bign operator+(const bign&,const bign&);/*加号运算符友元*/
    friend bign operator*(const bign&,const bign&);/*乘号运算符友元*/
    friend bign operator*(const bign&,int);/*高精度乘以低精度乘法友元*/
    friend bign operator-(const bign&,const bign&);/*减号运算符友元*/
    friend bign operator/(const bign&,const bign&);/*除法运算符友元*/
    friend bign operator%(const bign&,const bign&);/*模运算符友元*/
    friend bool operator<(const bign&,const bign&);/*逻辑小于符友元*/
    friend bool operator>(const bign&,const bign&);/*逻辑大于符友元*/
    friend bool operator<=(const bign&,const bign&);/*逻辑小于等于符友元*/
    friend bool operator>=(const bign&,const bign&);/*逻辑大于等于符友元*/
    friend bool operator==(const bign&,const bign&);/*逻辑等符友元*/
    friend bool operator!=(const bign&,const bign&);/*逻辑不等符友元*/
private:
    int len,s[maxn];
public:
    bign(){memset(s,0,sizeof(s));len=1;}
    bign operator=(const char* num)
    {
        int i=0,ol;
        ol=len=strlen(num);
        while(num[i++]=='0'&&len>1)
        len--;
        memset(s,0,sizeof(s));
        for(i=0;i<len;i++)
        s[i]=num[ol-i-1]-'0';
        return *this;
    }
    bign operator=(int num)
    {
        char s[maxn];
        sprintf(s,"%d",num);
        *this=s;
        return *this;
    }
    bign(int num){*this=num;}
    bign(const char* num){*this=num;}
    string str() const
    {
        int i;
        string res="";
        for(i=0;i<len;i++)res=char(s[i]+'0')+res;
        if(res=="")res="0";
        return res;
    }
};
/*(可选)基本逻辑运算符重载*/
bool operator<(const bign& a,const bign& b)
{
    int i;
    if(a.len!=b.len)return a.len<b.len;
    for(i=a.len-1;i>=0;i--)
        if(a.s[i]!=b.s[i])
    return a.s[i]<b.s[i];
    return false;
}
bool operator>(const bign& a,const bign& b){return b<a;}
bool operator<=(const bign& a,const bign& b){return !(a>b);}
bool operator>=(const bign& a,const bign& b){return !(a<b);}
bool operator!=(const bign& a,const bign& b){return a<b||a>b;}
bool operator==(const bign& a,const bign& b){return !(a<b||a>b);}
/*(可选)加法运算符重载*/
bign operator+(const bign& a,const bign& b)
{
    int i,max=(a.len>b.len?a.len:b.len),t,c;
    bign sum;
    sum.len=0;
    for(i=0,c=0;c||i<max;i++)
    {
        t=c;
        if(i<a.len)t+=a.s[i];
        if(i<b.len)t+=b.s[i];
        sum.s[sum.len++]=t%10;
        c=t/10;
    }
    return sum;
}
/*(可选)乘法运算符重载(高精度乘高精度)*/
bign operator*(const bign& a,const bign& b)
{
    int i,j;
    bign res;
    for(i=0;i<a.len;i++)
    {
        for(j=0;j<b.len;j++)
        {
            res.s[i+j]+=(a.s[i]*b.s[j]);
            res.s[i+j+1]+=res.s[i+j]/10;
            res.s[i+j]%=10;
        }
    }
    res.len=a.len+b.len;
    while(res.s[res.len-1]==0&&res.len>1)res.len--;
    if(res.s[res.len])res.len++;
    return res;
}
/*高精度乘以低精度(注意:必须是bign*int顺序不能颠倒,要么会与高精度乘高精度发生冲突*/
bign operator*(const bign& a,int b)
{
    int i,t,c=0;
    bign res;
    for(i=0;i<a.len;i++)
    {
        t=a.s[i]*b+c;
        res.s[i]=t%10;
        c=t/10;
    }
    res.len=a.len;
    while(c!=0)
    {
        res.s[i++]=c%10;
        c/=10;
        res.len++;
    }
    return res;
}
/*(可选)减法运算符重载*/
bign operator-(const bign& a,const bign& b)
{
    bign res;
    int i,len=(a.len>b.len)?a.len:b.len;
    for(i=0;i<len;i++)
    {
        res.s[i]+=a.s[i]-b.s[i];
        if(res.s[i]<0)
        {
            res.s[i]+=10;
            res.s[i+1]--;
        }
    }
    while(res.s[len-1]==0&&len>1)len--;
    res.len=len;
    return res;
}
/*(可选)除法运算符重载(注意:减法和乘法运算和>=运算符必选)*/
bign operator/(const bign& a,const bign& b)
{
    int i,len=a.len;
    bign res,f;
    for(i=len-1;i>=0;i--)
    {
        f=f*10;
        f.s[0]=a.s[i];
        while(f>=b)
        {
            f=f-b;
            res.s[i]++;
        }
    }
    while(res.s[len-1]==0&&len>1)len--;
    res.len=len;
    return res;
}
/*(可选)模运算符重载(注意:减法和乘法运算和>=运算符必选)*/
bign operator%(const bign& a,const bign& b)
{
    int i,len=a.len;
    bign res,f;
    for(i=len-1;i>=0;i--)
    {
        f=f*10;
        f.s[0]=a.s[i];
        while(f>=b)
        {
            f=f-b;
            res.s[i]++;
        }
    }
    return f;
}
/*(可选)X等运算符重载(注意:X法必选)*/
bign& operator+=(bign& a,const bign& b)
{
    a=a+b;
    return a;
}
bign& operator-=(bign& a,const bign& b)
{
    a=a-b;
    return a;
}
bign& operator*=(bign& a,const bign& b)
{
    a=a*b;
    return a;
}
bign& operator/=(bign& a,const bign& b)
{
    a=a/b;
    return a;
}
/*可选前缀++/--与后缀++/--(注意:加法必选)*/
bign& operator++(bign& a)
{
    a=a+1;
    return a;
}
bign& operator++(bign& a,int)
{
    bign t=a;
    a=a+1;
    return t;
}
bign& operator--(bign& a)
{
    a=a-1;
    return a;
}
bign& operator--(bign& a,int)
{
    bign t=a;
    a=a-1;
    return t;
}
istream& operator>>(istream &in,bign& x)
{
    string s;
    in>>s;
    x=s.c_str();
    return in;
}
ostream& operator<<(ostream &out,const bign& x)
{
    out<<x.str();
    return out;
}
bign g[10001];
int main()
{
    ios::sync_with_stdio(false);
    g[0]=0;
    g[1]=1;
    bign pow2=2;
    int ind=3,cnt=2;
    for(int i=2;i<=10000;i++)
    {
        g[i]=g[i-1]+pow2;
        if(i==ind)
        {
            cnt++;
            ind=cnt*(cnt+1)/2;
            pow2=pow2*2;
        }
    }
    int n;
    while(cin>>n)
    {
        cout<<g[n]<<endl;
    }
    return 0;
}

思路:
设f[i]为有i个盘子三个柱子汉诺塔的移动步数,g[i]为四个柱子。
那么按照题目中的要求,先把上面k个盘子挪到第四盘子上,有g[k]中挪法,将剩下的盘子挪到第三个盘子上,有f[i-k]中挪法,在把第四个柱子上的盘子挪到第三个盘子上,有g[k]中挪法。所以g[i]=min(g[k]*2+f[i-k])这里0<=k< i
但是此题数据量很大,有10000个。那么需要用到高精度模板,如果是字符串计算高精度模板的时间复杂度再乘上枚举k的时间复杂度,会超时。

在网上搜了搜方法,思路都一样,只不过都是在找规律,最后得到的规律是
f[i]=f[i-1]+2^(m-1)次幂
这里的m表示i属于第m组数
m的解释如下
i=1 m=1
………..
i=2 m=2
i=3 m=2
………..
i=4 m=3
i=5 m=3
i=6 m=3
………….
如下类推,也就是求前n项和最后一个数和前一个数之间的区间
然后使用打表即可

这里找到个4柱汉诺塔的问题,没毛卵用
http://www.cnblogs.com/fanzhidongyzby/archive/2012/07/28/2613173.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值