Dividing the Path POJ - 2373(单调队列优化dp)

给出一个n长度的区间,然后有一些小区间只能被喷水一次,其他区间可以喷水多次,然后问你要把这个区间覆盖起来最小需要多少喷头,喷头的半径是[a, b]。

对于每个只能覆盖一次的区间,我们可以把他中间的部分标记起来,每次只在他的两端放置喷头,中间的点不能放置多余的喷头,然后找状态方程

dp[i] = 到第i个位置最少的喷头

然后dp[i] = min(dp[j])+1 2*b<=i-j<=2*a

然后用单调队列维护最小值,就可以了

#include<map>
#include<set>
#include<ctime>
#include<cmath>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define first fi
#define second se
#define lowbit(x) (x & (-x))

typedef unsigned long long int ull;
typedef long long int ll;
const double pi = 4.0*atan(1.0);
const int inf = 0x3f3f3f3f;
const int maxn = 1000005;
const int maxm = 12;
using namespace std;

int n, m, T, tol;
int dp[maxn];
bool vis[maxn];
deque<int > q;

void init() {
    memset(dp, inf, sizeof dp);
    memset(vis, 0, sizeof vis);
}

int main() {
    while(~scanf("%d%d", &m, &n)) {
        init();
        int a, b;
        scanf("%d%d", &a, &b);
        for(int i=1; i<=m; i++) {
            int be, en;
            scanf("%d%d", &be, &en);
            for(int j=be+1; j<en; j++)    vis[j] = true;
        }
        if(n&1) {
            printf("-1\n");
            continue;
        }
        dp[0] = 0;
        q.push_back(0);
        for(int i=0; i<=n; i+=2) {
            while(!q.empty() && i - 2*a >= 0 && dp[i - 2*a] < dp[q.back()])    q.pop_back();
            q.push_back(i-2*a);
            if(vis[i] || i < 2*a)    continue;
            while(!q.empty() && q.front() + 2*b < i)    q.pop_front();
            dp[i] = dp[q.front()] + 1;
        }
        if(dp[n] >= inf)    printf("-1\n");
        else    printf("%d\n", dp[n]);
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/H-Riven/p/9363552.html