/* 魔法少女
时间限制: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;
}