码迷,mamicode.com
首页 > 其他好文 > 详细

19-魔法上楼梯

时间:2017-08-25 21:44:48      阅读:349      评论:0      收藏:0      [点我收藏+]

标签:描述   文件   string   cst   [1]   需要   amp   ops   iostream   

/*                                                            魔法少女
                      时间限制:1000 ms  |  内存限制:65535 KB
                             难度:3

描述
    前些时间虚渊玄的巨献小圆着实火了一把。 在黑长直(小炎)往上爬楼去对抗魔女之夜时,她遇到了一个问题想请你帮忙。 因为魔女之夜是悬浮在半空的,所以她必须要爬楼,而那座废墟一共有n层,而且每层高度不同,这造成小炎爬每层的时间也不同。不过当然,小炎会时间魔法,可以瞬间飞过一层或者两层[即不耗时]。但每次瞬移的时候她都必须要至少往上再爬一层(在这个当儿补充魔力)才能再次使用瞬移。爬每单位高度需要消耗小炎1秒时间。 消灭魔女之夜是刻不容缓的,所以小炎想找你帮她找出一种最短时间方案能通往楼顶。

    输入
        本题有多组数据,以文件输入结尾结束。
        每组数据第一行一个数字N(1 <= N <= 10000),代表楼层数量。
        接下去N行,每行一个数字H(1 <= H <= 100),代表本层的高度。
    输出
        对于每组数据,输出一行,一个数字S,代表通往楼顶所需的最短时间。
    样例输入

        5
        3
        5
        1
        8
        4

    样例输出

        1



题目的本质是:已知一个包含n个正整数的序列h[0],h[1]……h[n-1],求一个最小子序列,满足:
(1) 子序列中任意相邻的两个数,在原序列中的位置差小于等于3
(2) 所有满足条件(1)的序列中,最小子序列中的数的和最小。


例如以下测试数据:
10
3 5 1 8 4 2 5 6 1 3
有多种子序列选择,例如:
3 5 1 8 4 2 5 6 1 3(全选,总和为38)
3   1   4 2     1 3(和为14)
    1     2     1   (和为4)
3       4 2     1   (非法,3和4在原序列中的位置差大于3)


一般从活动的最后一步往前推导状态转移方程。
直观地,我们会想到这样的状态转移方程:
设f[i]表示在h[0...i]这个整数序列中,选出来的最小子序列是多少。f[i]的取值跟f[i-1],甚至f[i-2]、f[i-3]……有关。
f[i] = min { f[i - 1] + h[i]; // 取第i个数
             f[i - 1];   // 不取第i个数。oops!
             ……}
oops,我们在考虑不取第i个数的时候碰到问题了。因为我们不知道f[i-1]的最小子序列是怎么取的。如果f[i-1]的最小子序列没有取h[i-1]和h[i-2],那f[i]就不能等于f[i-1]了,否则违反条件(1)。
所以这里我们需要改一下:设f[i]表示在h[0...i]中选出的最小子序列,且第i个数必选。于是,
f[i] = min { f[i-1] , // 取f[i-1]
             f[i-2] , // 不取f[i-1]了,取f[i-2]
             f[i-3] , // f[i-1]和f[i-2]都不取,取f[i-3]
            } + h[i]; //h[i]必取
我们知道,在h[0...n-1]取出来的子序列中,必满足以下三种情况之一:
(1) 取h[n-1],则最小子序列为f[n-1]
(2) 不取h[n-1], 取h[n-2],则最小子序列为h[n-2]
(3) 不取h[n-1]和h[n-2],取h[n-3],则最小子序列为h[n-3]
*/


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[10005];
int a[10005];

int main(){
    int n;
    while(~scanf("%d", &n)){
        memset(dp, 0x3f3f3f3f, sizeof(dp));
        for(int i = 3; i < 3 + n; i++)    
            scanf("%d", &a[i]);
        dp[0] = dp[1] = dp[2] = 0;
        a[0] = 0;
         for(int i = 3; i < 3 + n; i++){
            dp[i] = min(min(dp[i - 1], dp[i - 2]), dp[i - 3]) + a[i];
        }
        printf("%d\n", min(min(dp[n + 2], dp[n + 1]), dp[n]));
    }
    return 0;
}

/*思路:用动态规划求解 dp[0][i]表示少女在i层,并且可以使用魔法,dp[1][i]表示少女在i层,但是不可以使用魔法

状态转移方程:

            dp[1][i]=min(dp[0][i-2],dp[0][i-1]);
            dp[0][i]=min(dp[1][i-1]+a[i],dp[0][i-1]+a[i]);
*/

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[2][10005];
int a[10005];

int main(){
    int n;
    while(~scanf("%d", &n)){
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++){
            scanf("%d", &a[i]);
        }
        dp[0][0] = 0;
        dp[1][1] = dp[1][2] = 0;
        for(int i = 1; i <= n; i++){
            dp[0][i] = min(dp[0][i - 1], dp[1][i - 1]) + a[i];
            if(i >= 3)
                dp[1][i] = min(dp[0][i-1], dp[0][i-2]);
        }
        printf("%d\n", min(dp[0][n], dp[1][n]));
    }
    return 0;
}

19-魔法上楼梯

标签:描述   文件   string   cst   [1]   需要   amp   ops   iostream   

原文地址:http://www.cnblogs.com/zhumengdexiaobai/p/7429711.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!