hdu2587(递推)

本文探讨了一种复杂的汉诺塔变体——O_O汉诺塔问题,该问题涉及不同大小的盘子及其数量的变化。文章通过递归算法详细阐述了解决方案,并提供了具体的实现代码,展示了如何在限制条件下找到移动所有盘子所需的最小步骤。
摘要由CSDN通过智能技术生成

目前做过的最纠结的一道递推题。

情况比较多,比较复杂。。。

这题最主要的还是要推出当m=2 时和m>2时,用什么方法最优。

给个数据

n=3,m=2   需要48

n=3,m=3 需要81

如果在纸上把这两种情况推出来,这题就容易找到递推。

m=1,就是最基础的汉诺塔递推了。

很O_O的汉诺塔

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 561    Accepted Submission(s): 67


Problem Description
O_O汉诺塔包含n种不同大小盘,每种大小m个; 要求每次仅移动一个盘,不允许一个较大的盘放在较小盘上。 并且要求最后排列所有相等大小盘按原来从上到下次序,并且只能按规定的方向搬运,如图(比如A直接搬运到C是不允许的!). 求已知n,m的情况下 从A搬运到C所有盘所用的最少次数。

 

 

Input
每行输入n和m两个整数 0<n<1000,0<m<100;
 

 

Output
每行输出对应解,为避免高精度将结果对20090308取模.
 

 

Sample Input
1 3 2 4
 

 

Sample Output
6 28
 

 

Source
 
//
//  main.cpp
//  hdu2587
//
//  Created by 陈加寿 on 16/3/17.
//  Copyright © 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 1100
#define MOD 20090308

long long dp[N][2];

//来一个统计,每个小块被搬多少次
long long save[N][N][2];
long long m;
int tn;
int g[N];

long long dfs(int n,int flag)
{
    if(dp[n][flag]!=-1) return dp[n][flag];
    if(flag == 1)
    {
        dp[n][flag] = (2*dfs(n-1,1)+2*m+dfs(n-1,0))%MOD;
        save[n][n][flag]=2;
        if(n== tn)
        {
            for(int i=1;i<=n-1;i++)
            {
                save[n][i][flag]+=save[n-1][i][1]+save[n-1][i][0];
                save[n][i][flag]%=2;
            }
        }
        else
        {
            for(int i=1;i<=n-1;i++)
            {
                save[n][i][flag]+=2*save[n-1][i][1]+save[n-1][i][0];
                save[n][i][flag]%=2;
            }
        }
    }
    else
    {
        dp[n][flag] = (2*dfs(n-1,1)+m)%MOD;
        save[n][n][flag] = 1;
        for(int i=1;i<=n-1;i++)
        {
            save[n][i][flag] += 2*save[n-1][i][1];
            save[n][i][flag]%=2;
        }
    }
    return dp[n][flag];
}

int main() {
    int n;
    while(cin>>n>>m)
    {
        tn = n;
        //来测试一种方法。!
        memset(save,0,sizeof(save));
        memset(g,0,sizeof(g));
        memset(dp,-1,sizeof(dp));
        dp[1][0] = m;
        save[1][1][0] = 1;
        dp[1][1] = 2*m;
        save[1][1][1] = 2;
        dfs(n,1);
        //对于n
        if(m>2)
        {
            if(n<=2)
            {
                cout<<dp[n][1]<<endl;
            }
            else
            {
                cout<<(dp[n][1]+2*(dp[n-2][1]+dp[n-2][0]))%MOD<<endl;//进行一次调整。
            }
        }
        else if(m==2)
        {
            //m == 2时
            if(n<=2)
            {
                cout<<dp[n][1]<<endl;
            }
            else
            {
                //int cnt=1;
                //cout<<(dp[n][1]+2*(dp[n-2][1]+dp[n-2][0])-3*m*(n-2) )%MOD<<endl;//进行一次调整。
                //各种不对。
                long long tans=0;
                for(int i=n;i>1;i--)
                {
                    //把第i个块,从A放入C中
                    //第一步判断是否需要调整
                    if(g[i] == 1)
                    {
                        tans = (tans + dp[i-1][0] + dp[i-1][1])%MOD;//调整
                        for(int j=1;j<i;j++)
                        {
                            g[j] += save[i-1][j][0]+save[i-1][j][1];
                            g[j]%=2;
                        }
                    }
                    tans = (tans + dp[i-1][0] + dp[i-1][1]+2*m)%MOD;
                    for(int j=1;j<i;j++)
                    {
                        g[j] += save[i-1][j][0]+save[i-1][j][1];
                        g[j]%=2;
                    }
                }
                cout<<(tans+4)%MOD<<endl;//这样既然是对的,那么上面也是对的
            }
        }
        else// m == 1
        {
            cout<<dp[n][1]<<endl;
        }
//        for(int i=1;i<=n-2;i++)
//            save[n][i][1] += 2*save[n-2][i][0]+2*save[n-2][i][1];
//
//        for(int i=1;i<=n;i++)
//            cout<<i<<" "<<save[n][i][1]<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值