zoukankan      html  css  js  c++  java
  • UVa 10795

    题目

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1736


    题意

    汉诺塔问题,给定初始状态和最终状态,求最小步数(一定可行)

    思路

    本来以为是搜索,

    如刘书思路。

    由于只有三根柱子,假设n-1到i + 1号盘子都已经对齐,对于没有对齐的i号盘子,必要有两根柱子用于转移,一根是现在i所在的柱子,另一根是目的柱子,第三根空闲柱子存放其他无用但是还没对准的小盘子。也即小盘子只能存在空闲柱子上。也即最短路径就是略过已经堆放好的盘子->将无用小盘子堆在空闲柱子上->把最大的没对齐盘子放到应该在的地方这样一个循环。

    这样已经能够写出确定性程序,但还存在初始和最终状态都比较复杂的情况。

    想到中间状态一定会经过,而忽略掉最后一个还没对齐的最大盘子i的中间状态十分简单,是小盘子[0,...,i-1]在空闲柱子上的简单堆叠,那么可以发现 [0,..., i - 1].终止状态到中间状态的最小步数+ [0,..., i - 1]初始状态到中间状态的最小步数 + 1(移动第i个盘子所需步数) = 总最小步数

    而终止状态到中间状态的转换中,移动[0,..., i - 1]到空闲柱子的步数为移动[0, ... , i-2]到新空闲柱子的步数+把[0, ... , i - 2]移回来的步数 + 1(移动i-1),依此类推。(注意有时候不需要移动的情况)

    而把[0, ... , i - 2]移回来的步数是2^(i - 2) - 1。

    感想

    1. 一开始只想到了用搜索暴力做,后来稍微瞄了一眼,也觉得可以用双向bfs来做

    2. 之后想到了何为必然出现的状态,也即找到了正确的最短路径,但是还是面临着需要一段段对齐最终状态的问题,代码因此杂乱无章,刘书中提到的将初始和终止状态都转化为清晰简单的中间状态的方法大大减少了实现难度

    3. 在实现中一开始不是按照 1 + i-1初始->中间步数 + i-1终止->中间步数来做的,而是按照 i初始->中间步数 + i终止->中间步数,与实际情况不符。 

    #include <algorithm>
    #include <cassert>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <tuple>
    #include <set>
    #include <map>
    #include <cassert>
    #define LOCAL_DEBUG
    using namespace std;
    const int MAXN = 61;
    long long steps[MAXN];
    int pegOrg[MAXN];
    int pegAim[MAXN];
    
    long long mov(int * pegs, int pos, int desPeg) {
        if (pos < 0) return 0;
        if (pegs[pos] == desPeg)return mov(pegs, pos - 1, desPeg);
        return mov(pegs, pos - 1, 6 - desPeg - pegs[pos]) + steps[pos];
    }
    
    int main() {
    #ifdef LOCAL_DEBUG
        freopen("input.txt", "r", stdin);
        //freopen("output2.txt", "w", stdout);
    #endif // LOCAL_DEBUG
        int n;
        for (int i = 0; i < MAXN; i++) {
            steps[i] = 1L << i;
        }
        for (int ti = 1; scanf("%d", &n) == 1 && n; ti++) {
            for (int i = 0; i < n; i++)scanf("%d", pegOrg + i);
            for (int i = 0; i < n; i++)scanf("%d", pegAim + i);
            long long ans = 0;
            for (int i = n - 1; i >= 0; i--) {
                if (pegOrg[i] != pegAim[i]) {
                    ans = mov(pegOrg, i - 1, 6 - pegAim[i] - pegOrg[i]) + mov(pegAim, i - 1, 6 - pegAim[i] - pegOrg[i]) + 1;
                    break;
                }
            }
            printf("Case %d: %lld
    ", ti, ans);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    深入理解javascript的this关键字
    很简单的JQuery网页换肤
    有关垂直居中
    层的半透明实现方案
    常用meta整理
    web前端页面性能优化小结
    关于rem布局以及sprit雪碧图的移动端自适应
    mysql入过的坑
    日期格式化函数
    基于iframe父子页面传值的方法。
  • 原文地址:https://www.cnblogs.com/xuesu/p/10380750.html
Copyright © 2011-2022 走看看