魔法少女
-
描述
-
前些时间虚渊玄的巨献小圆着实火了一把。 在黑长直(小炎)往上爬楼去对抗魔女之夜时,她遇到了一个问题想请你帮忙。 因为魔女之夜是悬浮在半空的,所以她必须要爬楼,而那座废墟一共有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; }
-
本题有多组数据,以文件输入结尾结束。