noip2010烽火传递 单调队列优化

noip2010烽火传递

问题背景

(NOIP2010 初赛)

题目描述

烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确的传递,在m个烽火台中至少要有一个发出信号。现输入n、m和每个烽火台发出的信号的代价,请计算总共最少需要话费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递!!!

输入格式

第一行有两个数n,m分别表示n个烽火台,在m个烽火台中至少要有一个发出信号。
第二行为n个数,表示每一个烽火台的代价。 

输出格式

 一个数,即最小代价。 

样例输入1

5 3 




2

样例输出1

4

注释说明

对于50%的数据,M≤N≤1,000 。 对于100%的数据,M≤N≤ 100,000,Wi≤100。

#include<bits/stdc++.h>
using namespace std;
int n,m,a[100005],f[100005];
deque<int>q;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int k=1;k<=m;k++){
		f[k]=a[k];
	}
	for(int i=m+1;i<=n;i++){
		int d=0x7f7f7f7f;
		for(int j=i-m;j<=i-1;j++){
			d=min(d,f[j]);
		}
		f[i]=d+a[i];
	}int ans=0x7f7f7f7f;
	for(int i=n-m+1;i<=n;i++){
		ans=min(ans,f[i]);
	}
	printf("%d",ans);
}
/*
5 3 
1 
2 
5 
6 
2
*/

 单调队列优化

首先申请一个双端队列q

deque<int> q;

该元素过期(i-m>q.front()),队首元素就要出队。

if i<=m  f[i]=a[i];

else f[i]=f[q.front()]+a[i];
          

队列非空,并且当前元素a[p]比队尾元素小,那么队尾元素就要出队;

#include<bits/stdc++.h>
using namespace std;
int n,m,a[100005],f[100005],p=0x7f7f7f7f;
deque<int>q;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		while(!q.empty()&&i-m>q.front()){
			q.pop_front();
		}
		if(i<=m){
			f[i]=a[i];
		}
		else {
			f[i]=f[q.front()]+a[i];
			
		}
		while(!q.empty()&&f[i]<=f[q.back()]){
			q.pop_back();
		}			
		q.push_back(i);
	}
	int ans=p;
	for(int i=n-m+1;i<=n;i++){
		ans=min(ans,f[i]);
	}
	printf("%d",ans);
}
/*
#include<bits/stdc++.h>
using namespace std;
int n,m,a[100005],f[100005];
deque<int>q;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int k=1;k<=m;k++){
		f[k]=a[k];
	}
	for(int i=m+1;i<=n;i++){
		int d=0x7f7f7f7f;
		for(int j=i-m;j<=i-1;j++){
			d=min(d,f[j]);
		}
		f[i]=d+a[i];
	}int ans=0x7f7f7f7f;
	for(int i=n-m+1;i<=n;i++){
		ans=min(ans,f[i]);
	}
	printf("%d",ans);
}
5 3 
1 
2 
5 
6 
2
*/

*****撒花*****

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值