魔法少女

魔法少女

时间限制: 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]


AC代码:


 
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
	int n,i;
	int dp[10010],a[10010];
	while (~scanf("%d",&n))
	{
		for (i=3;i<=n+2;++i)
		{
			scanf("%d",&a[i]);
		}
		memset(dp,0x3f3f3f3f,sizeof(dp));
		dp[0]=0;dp[1]=0;dp[2]=0;
		a[0]=0;
		for (i=3;i<=n+2;++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;
}        


网上代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int main()
{
    int a,b,c,d,n,i,tt;
     while(scanf("%d",&n)!=EOF)
     {
         a=b=c=0;
         for(i=1;i<=n;i++)
         {
             scanf("%d",&tt);
             d=min(a,min(b,c))+tt;
             c=b;b=a;a=d;
         }
         printf("%d\n",min(a,min(b,c)));
     }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值