奇怪的汉诺塔
题面
思路
首先考虑三个柱子的汉诺塔;
假设当前有 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(i−1)∗2+1
+ 1 +1 +1指的是最后一个盘子,而 f ( i − 1 ) ∗ 2 f(i-1)*2 f(i−1)∗2则是因为 n − 1 n-1 n−1个盘子需要移动两次(先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(i−j)则是上面三塔模式的;
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;
}