奇怪的汉诺塔 && Four Column Hanoi Tower

奇怪的汉诺塔

题面

传送门
在这里插入图片描述
在这里插入图片描述

思路

首先考虑三个柱子的汉诺塔;

假设当前有 n n n个盘子;

 先把前n-1个盘子从A柱移到B柱,
 然后把A柱上剩的那一个盘子移动到C柱
 最后把B柱上的那n-1个盘子移动到C柱上

因此可以得到递推式 f ( i ) = f ( i − 1 ) ∗ 2 + 1 f(i)=f(i-1)*2+1 f(i)=f(i1)2+1

+ 1 +1 +1指的是最后一个盘子,而 f ( i − 1 ) ∗ 2 f(i-1)*2 f(i1)2则是因为 n − 1 n-1 n1个盘子需要移动两次(先A到B,再B到C)


接着考虑四个盘子的汉诺塔

 先把j个盘子在4塔模式下移动到B柱,
 然后把i-j个盘子在3塔模式下移动到D柱
(因为不能覆盖到B柱上,就等于只剩下A、C、D柱可以用)
 最后把j个盘子在4塔模式下移动到D柱

因此有递推式g[i] = min(g[i],2*g[j]+f[i-j]);

同理 g [ j ] ∗ 2 g[j]*2 g[j]2是因为要将 j j j个盘子在四塔模式下移动两次;

f ( i − j ) f(i-j) f(ij)则是上面三塔模式的;

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 1e4 + 10,M = 12;

int f[N],g[N];//三个柱子汉诺塔、四个柱子汉诺塔

void init(){
    for(int i=1;i<=M;++i){
        //将前i-1个移动到另一边
        //将最大的(第i个)放在目标处
        f[i] = f[i-1] * 2 + 1;
    }
    memset(g,0x3f,sizeof g);
    g[0] = 0;
    for(int i=1;i<=M;++i){
        for(int j=0;j<i;++j){
            //首先挪走j个塔,也就是有四个塔可以选择
            //然后再挪走剩下的n-j个塔,此时有三个塔可以选择
            g[i] = min(g[i],2*g[j]+f[i-j]);
        }
    }
}

void solve(){
    int n;
    //cin >> n;
    for(int i=1;i<=12;++i)
        cout << g[i] << '\n';
}
int main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    init();
    int t = 1;
    //cin >> t;
    while(t--)
        solve();
    return 0;
}

Four Column Hanoi Tower

题面

传送门
在这里插入图片描述
在这里插入图片描述

思路

可以发现和上面那题是一模一样的,只不过这题数据量比较大;

因此我们需要高精度;

这里有个玄学优化,我是看了其他人的代码;

以及多柱汉诺塔最优算法设计探究


int j=max(0,i-150)

因为 n 2 n^2 n2的复杂度再加上高精度是会TLE的;

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long ll;

const int N = 1e4 + 10,M = 1e4;


bool compare(string a, string b) //判断a是否小于b -->如果相等记得特判为0
{
    if (a.size() < b.size()) return true;  //当a的位数比b小时,a比b小
    if (a.size() > b.size()) return false; //当a的位数比b大时,a比b大
    for (unsigned int i = 0; i < a.size(); ++i)     //剩余情况就是a的位数与b相同
    {
        if (a[i] > b[i]) return false;     //逐位比较
        if (b[i] > a[i]) return true;
    }
    return false; //最后当a与b的各个位的数字都相同时,a等于b
}

//高精度加法
//只能是两个正数相加
string add(const string& A, const string& B)
{
    string C;
    int t = 0;
    for (int i = A.size()-1, j = B.size()-1; i >= 0 || j >= 0 || t > 0; i--, j--)
    {
        if (i >= 0) t += (A[i] - '0');
        if (j >= 0) t += (B[j] - '0');
        C += ((t % 10) + '0');
        t /= 10;
    }

    reverse(C.begin(), C.end());
    return C;
}


string f[N],g[N];//三个柱子汉诺塔、四个柱子汉诺塔

void init(){
    for(int i=1;i<=M;++i){
        //将前i-1个移动到另一边
        //将最大的(第i个)放在目标处
        f[i] = add(f[i-1],f[i-1]);
        f[i] = add(f[i],"1");
    }
    g[0] = "0";
    for(int i=1;i<=M;++i) g[i] = "0";
    string tmp;
    for(int i=1;i<=M;++i){
        for(int j=max(0,i-150);j<i;++j){
            //首先挪走j个塔,也就是有四个塔可以选择
            //然后再挪走剩下的n-j个塔,此时有三个塔可以选择
            tmp = add(g[j],g[j]);
            tmp = add(tmp,f[i-j]);
            if(g[i] == "0" || compare(tmp,g[i])){
                g[i] = tmp;
            }
        }
    }
}

void solve(){
    int n;
    cin >> n;
    cout << g[n] << '\n';
}
int main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    init();
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值