Codeforces Round #587 (Div. 3) F. Wi-Fi(單調隊列優化DP)

題目:https://codeforces.com/contest/1216/problem/Fc++

題意:一排有n個位置,我要讓全部點都能聯網,我有兩種方式聯網,第一種,我直接讓當前點聯網,花費爲i,第二種,若是當前點的值爲1,表明當前點能夠放置一個路由器,範圍 [i-k,i+k]都能連上網,花費爲i,求最小花費是全部點都能連上網spa

思路:這個很容易看出是一個DP,咱們設立dp[i],爲前i個位置都能連上網的最小花費,由於設立一個路由器左右範圍均可以聯網,因此咱們考慮設立路由器的右端點,若是i-k能夠設立路由器,咱們 dp[i]=min(dp[i],dp[j]+i-k) 可是咱們這個j怎麼肯定呢,確定是前面的最小值來的,咱們在 [i-2*k-1,i] 裏面尋找最小值,而後取最優 ,咱們能夠用單調隊列求得最大值,用線段樹也是能夠的code

 

#include<bits/stdc++.h>
#define maxn 500005
#define mod 1000000007
using namespace std;
typedef long long ll;
deque<int> d;
int n,k;
char str[maxn];
ll dp[maxn];
int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",str+1);
    d.push_back(0);//必須加,由於咱們設立第一個路由器的時候可能會用到0,可是第一個位置的值就是1了
    for(int i=1;i<=n+k;i++){
        dp[i]=dp[i-1]+i;//若是當前這個點用第一種方式聯網的話
        if(i-k>0&&str[i-k]=='1'){//若是能夠用第二種方式聯網
            while(!d.empty()&&d.front()<i-2*k-1) d.pop_front(); //利用單調隊列把不是該範圍的數先踢出去
            dp[i]=min(dp[i],dp[d.front()]+i-k);//由於是i-k這個位置設立路由器,因此+(i-k)
        }        
        while(!d.empty()&&dp[d.back()]>=dp[i]) d.pop_back();
        d.push_back(i);
    }
    ll mx= 0x3f3f3f3f3f3f3f3f;
    for(int i=n;i<=n+k;i++) mx=min(mx,dp[i]);//取最優
    cout<<mx;
} 
相關文章
相關標籤/搜索