[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;
}

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM