题意: 熟知的汉诺塔问题的改版。现在给4根柱子,要求按这个方式移动:现将最上面k个移到某根柱子上,然后用剩下3根柱子做经典汉诺塔问题,然后再把移开的k个移回来。问最少需要移动多少次。数据范围10000。
解法: 设g[x]为汉诺塔3石柱的最小移动步数,即g[x] = 2^x-1。设f[x]为4石柱的最小移动步数,则
f[x] = max(f[k]*2+g[x-k]),0<=k<x
显然这个方程的复杂度O(n^2)不足以应付题目的数据范围,于是,打表,找规律- -。
具体过程见这里。
吐槽一下,这竟然在UVA的DP分类里啊,什么题目都有啊。。。
数据特别大要用到高精度,只用到加法还是比较好敲的。下面是用C++写的代码
/* **********************************************
Author : Nero
Created Time: 2013-8-28 12:30:17
Problem id : UVA 10254
Problem Name: The Priest Mathematician
*********************************************** */
/*
高精度,打表找规律。
*/
#include <stdio.h>
#include <string>
#include <string.h>
#include <algorithm>
using namespace std;
#define REP(i,a,b) for(int i=(a); i<(int)(b); i++)
#define clr(a,b) memset(a,b,sizeof(a))
const int maxn = 4000 ;
char s1[105] , s2[105] , s3[105] ;
struct bign {
short s[maxn] , len ;
bign () { memset ( s ,0 , sizeof ( s ) ) ; len = 1 ; }
bign operator = (const char *num) {
len = strlen ( num ) ;
for ( int i = 0 ; i < len ; i ++ ) s[i] = num[len-i-1] - '0' ;
return *this ;
}
bign operator = (int num) {
char s[maxn] ;
sprintf (s , "%d" , num);
*this = s ;
return *this ;
}
bign(const char *num) { *this = num ; }
bign(int num) { *this = num ; }
string str () const {
string res ;
res = "" ;
for (int i = 0; i < len; i ++) res = (char) (s[i] + '0') + res ;
if (res == "") res = '0';
return res ;
}
bign operator + (const bign& b) const {
bign c ;
c.len = 0 ;
for(int i = 0, g = 0; g || i < max (len, b.len); i ++) {
int x = g ;
if (i < len) x += s[i] ;
if (i < b.len) x += b.s[i] ;
c.s[c.len++] = x % 10 ;
g = x / 10 ;
}
return c ;
}
void print() {
for(int i = len - 1; i >= 0; i --) printf("%hd", s[i]);
printf("\n");
}
}table[10100];
int main() {
bign g = 1;
table[0] = 0;
for(int i = 1, j = 1; i <= 10000; ) {
for(int k = 1; k <= j; k ++, i ++) {
table[i] = table[i-1] + g;
}
j ++;
g = g + g;
}
int n;
while(~scanf("%d", &n)) table[n].print();
return 0;
}