題目: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; }