题目Problem
[NOIP2010初赛]烽火传递
Time Limit: 1000ms Memory Limit: 131072KB描述Descript.
烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确的传递,在m个烽火台中至少要有一个发出信号。现输入n、m和每个烽火台发出的信号的代价,请计算总共最少需要话费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递!!!
输入Input
第一行有两个数n,m分别表示n个烽火台,在m个烽火台中至少要有一个发出信号。
第二行为n个数,表示每一个烽火台的代价。
第二行为n个数,表示每一个烽火台的代价。
输出Output
一个数,即最小代价。
样例Sample
输入数据
5 3 1 2 5 6 2
输出数据
4
备注Hint
1<=n,m<=1,000,000
这题在整理动态规划题的时候遇到了
看了原题,发现数据范围根本不是动态规划hold住的,最终优化为单调队列
最初始的动规:设f[i]表示点燃当前位置烽火台,且前i个满足要求的最小代价。
显然就有f[i]=min(f[j])+a[i](i-m<=j<=i-1)。
当然,这会超时,所以要有优化。
优化一:肯定是从前m个里选小的,涉及到区间最小值,可用线段树,时间复杂度将为O(n log m)。
优化二:同样因为要选前m个最小的,使用单调队列,队列里存有不超过m个长度单位的值,每次取队首,进队时维护队列使其单调不下降,复杂度将为O(n)。
单调队列
需要注意
1.队列中存储的不是元素本身,而是输入的序号
2.队列要保持单调递增,只有这样,才能保证队列中每个元素都有成为最小元素的可能
#include<iostream> using namespace std; #define MAXN 1000010 int n,m,l,r,a[MAXN],f[MAXN],q[MAXN<<1]; int main(){ cin>>n>>m; for(int i=1;i<=n;i++)cin>>a[i]; l=r=0; for(int i=1;i<=n;i++){ while(l<r&&i-q[l]>m)l++;//队列不为空且队首元素不合法 f[i]=f[q[l]]+a[i];//用队首更新答案 while(l<r&&f[q[r]]>f[i])r--;//更新队尾,使队列保持单调性 q[++r]=i;//进队 } int ans=0x7fffffff; for(int i=n-m+1;i<=n;i++)ans=min(ans,f[i]); cout<<ans; return 0; }