[jzoj]1229. Hanoi(DP决策最优性优化)

来源:互联网 发布:淘宝怎么做到包邮 编辑:程序博客网 时间:2024/06/05 02:22

Description

Mpq 小时候只玩过俄罗斯方块这个经典的小游戏,当时他还不知道Hanoi 究竟是
什么东西。话说当Mpq 第一次认识Hanoi 是在初三那年的联赛。由于Mpq 之前并不知
道Hanoi 是什么东西,所以那一年他做完前三题之后很郁闷地坐了1 个半小时。。。
好了,现在Mpq 成长了,他已经解决当年联赛那道Hanoi 了,在前几个月,他又
发现一道关于Hanoi 的题目了,很幸运的是这个题目他知道怎么做了。。。然后为了让大
家体验一下Mpq 初三联赛那种无奈的感觉,所以,这道题就神奇地出现在你们眼前。
Task:赶快AC 这道题目,然后你就可以狂鄙视,甚至是无视Mpq 的存在了!!!
哎,吹着吹着发现我还没把题目写下来。。。。
现在给你M 根柱子,初始的时候有N 个大小不一样的盘插在第一根柱子上面。同
样地,规格大的盘子不能放在规格比它小的盘子上面。问最少需要多少次的移动才能将
这N 个盘从第一根柱子移动到最后一根柱子上面?

Input

输入文件的第一行有两个整数n,m(1≤n≤100000,3≤m≤10),分别表示有n 个盘子和m
根柱子。

Output

输出文件只有一行,一个整数,表示最少的移动次数。保证这个移动次数不会超过
2^63-1。

Sample Input

4 3

Sample Output

15

Data Constraint
数据约定:
对于30%的数据,M=3
对于80%的数据,1≤N≤100,3≤M≤10
对于100%的数据,1≤N≤100000,6≤M≤10


Solution

这是一道很牛逼的DP.

我们设f[i][j]表示把j个盘子从第一根柱子移到第i根柱子的最少方案数.

首先,一个很显然的结论:

柱子相对多,盘子固定,肯定方案相对优.

相对是因为如果把当柱子已经大于盘子,那么再增加柱子都不会更优.

发现,如果每次多增一个柱子,盘子相同,那么方案数更新的次数难以计算.

那我们考虑柱子相同,如果每次多增加一个盘子,方案数应该怎么算?

显然,就是f[i][j]=Min{f[i][k]+f[i1][jk]} .

但是k是枚举的,这样更新状态是O(n)的,总时间复杂度O(n2m)

我们考虑最优状态应该是怎样的?

记录一个g[i][j]表示当有i根柱子,j个盘子时的最优决策,即是先把前g[i][j]个盘子从第一根柱子移到其他柱子去,然后再把剩下的jg[i][j]个盘子移到第i根柱子去.

那么假设我现在由f[i][j]>f[i][j+1],如何转移呢?

很显然就是利用g[i][j],因为当前多了一个盘子,我不可能先移前g[i][j]1个盘子,否则g[i][j]不可能是最优的.

同理,我也不可能移前g[i][j]+2个盘子,否则g[i][j]也不是最优的,这里可以感性+理性的理解,无需证明.

那么转移就分两种,处理一下边界就好了.

#include <iostream>#include <cstring>#include <cstdio>#define maxn 100001#define maxm 61#define LL long long#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;LL n,m,i,j,k;LL f[maxm][maxn],g[maxm][maxn];int main(){    scanf("%lld%lld",&n,&m);    memset(f,30,sizeof(f));    f[2][0]=0;    f[2][1]=1;    f[3][0]=0;    f[3][1]=1;      //f[3][2]=3; f[3][3]=7; f[3][4]=15;    fo(i,3,m)    {        f[i][1]=1;        g[i][1]=0;        fo(j,2,n){                      k=g[i][j-1];            if ((f[i][k]<<1)+f[i-1][j-k]<(f[i][k+1]<<1)+f[i-1][j-k-1])            {                f[i][j]=(f[i][k]<<1)+f[i-1][j-k];                g[i][j]=k;            } else            {                f[i][j]=(f[i][k+1]<<1)+f[i-1][j-k-1];                g[i][j]=k+1;            }        }    }    printf("%lld",f[m][n]);}