您的位置:首页 > 其它

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

2017-08-15 19:50 190 查看
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[i−1][j−k]} .

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

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

记录一个g[i][j]表示当有i根柱子,j个盘子时的最优决策,即是先把前g[i][j]个盘子从第一根柱子移到其他柱子去,然后再把剩下的j−g[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]
);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: