隐藏页面特效

[NOIP2010初赛]烽火传递+单调队列详细整理

P1313 [NOIP2010初赛]烽火传递
时间: 1000ms / 空间: 131072KiB / Java类名: Main

描述

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

输入格式

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

输出格式

一个数,即最小代价。

测试样例1

输入

5 3 
1 2 5 6 2

输出

4

备注

1<=n,m<=1,000,000

先上50分代码:

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=5e3+10; const int inf=2e9; int n,m,a[N*100]; int dp[N][N]; int dfs(int x,int y){//dp[x][y]表示选到第x个,已经空了y个(没有发出信号) if(y==m) return inf; if(x==n+1) return 0; if(dp[x+1][y+1]!=-1) dp[x][y]=dp[x+1][y+1]; else dp[x][y]=dfs(x+1,y+1); if(dp[x+1][0]!=-1) dp[x][y]=min(dp[x][y],dp[x+1][0]+a[x+1]); else dp[x][y]=min(dp[x][y],dfs(x+1,0)+a[x+1]); return dp[x][y]; } int main(){ memset(dp,-1,sizeof dp); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); dfs(0,0); printf("%d",dp[0][0]); return 0; }

前言:

地址:https://baike.baidu.com/link?url=Q1N9tP7fq-tZ3K8K6WWlPChsloP_NHdd_Yydv74xa4NtJZw6uKYxrRM5LndT7foxrrRjQJe6PoeTVdtc9_62uSuKZwdmvpc_-G3eAVkXQyHv_Py9hO4iox3k2yell059

自己对于单调队列的一点理解:

for(;l<r&&i-q[l]>m;l++);//队列里一定要有一个元素且不合法才出队(删除前面) f[i]=f[q[l]]+a[i];//用队首来更新当前f[i]的答案 for(;l<r&&f[q[r]]>f[i];r--);//用当前的f[i]去更新队尾,使队列保持单调性(删除后面) q[++r]=i;//进队

解析:

设f[i]表示点燃当前位置烽火台,且前i个满足要求的最小代价。 
显然就有f[i]=min(f[j])+a[i](i-m<=j<=i-1)。 
当然,这会超时,所以要有优化。 
优化一:肯定是从前m个里选小的,涉及到区间最小值,可用线段树,时间复杂度将为O(n log m)。 
优化二:同样因为要选前m个最小的,使用单调队列,队列里存有不超过m个长度单位的值,每次取队首,进队时维护队列使其单调不下降,复杂度将为O(n)。

(这里主要讲解 优化二即单调队列)

AC代码:

 

#include<cstdio> #include<algorithm> using namespace std; inline const int read(){ register int x=0,f=1; register char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } const int N=1e6+10; int n,m,l,r,a[N],f[N],q[N<<1]; int main(){ n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); l=r=0; for(int i=1;i<=n;i++){ for(;l<r&&i-q[l]>m;l++); f[i]=f[q[l]]+a[i]; for(;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]); printf("%d",ans); return 0; }

 

 

 

 

 

 


__EOF__

本文作者shenben
本文链接https://www.cnblogs.com/shenben/p/6039348.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   神犇(shenben)  阅读(3178)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示