poj2373

http://acm.hust.edu.cn/vjudge/problem/16596

/*
solution:
    单调队列优化dp
    首先定义状态:f[i]表示恰好覆盖到i时,用了多少喷头。其次定义状态转移方程:
    可以很容易看出当A<=j-i<=B时(A,B均为直径),f[j]=min(f[i])+1。但是
    A和B之间最多可以相差1998,所以若是枚举的话肯定超时!所以可以考虑用队列来
    进行优化。在求j时将满足j-B<=i<=j-A的i点压进去。每次要求j时就将队头元素
    取出,若是小于j-B,说明是求解上一步循环所压入的点,对这次求解无用,抛弃之。
    但千万要保证在队列内的所有点不能超过j-A!求出f[j]后记得将下一步求解所需要
    的初始值,f[j-B+2]压入。
    另外还有一些细节处理。因为由于直径等于半径2倍的关系,所以在求解过程中之考虑
    偶数点,可以先做判断,l如果是奇数就直接无解。
    题目还要求一头奶牛活动范围必须在一个喷头内。所以对给出的闭区间,除了首尾两端
    中间所有点都不能求解。可以用一个bool数组来表示。

note:
    记住定义的是小顶堆啊!
    题目hint给出的示意图有毒!是错误的!!WA了半天!闭区间两端是点的位置。

date:
    2016/8/15
*/
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>

using namespace std;
const int maxn = 1000 + 5;
const int maxl = 1000000;
const int INF = 1 << 30;

struct Node {
    int x, fx;
    Node(int x, int fx) : x(x), fx(fx) {}
    Node() {}
    bool operator < (const Node& rhs) const {
        return fx > rhs.fx; //记住定义小顶堆
    }
};
int n, l;       //奶牛以及道路的长度
int A, B, s, e; //喷泉的喷洒半径以及每头奶牛的活动范围
bool cowThere[maxl];
int f[maxl];    //f[i]表示恰好覆盖长度为i时候的所用喷头

int main()
{
    //freopen("input.txt", "r", stdin);
    while(~scanf("%d%d", &n, &l)) {
        if(l % 2 != 0)  printf("-1\n");
        else {
            scanf("%d%d", &A, &B);
            memset(cowThere, 0, sizeof(cowThere));

            for(int i = 0; i < n; i++) {
                scanf("%d%d", &s, &e);
                for(int j = s+1; j < e; j++)    //hint插图误导人!
                    cowThere[j] = true;         //奶牛活动范围给出的描述是个闭区间,首尾两个数值是点。
            }
            A *= 2;     B *= 2;

        //  for(int i = 0; i <= l; i++) printf("%d ", cowThere[i]); printf("\n");

            for(int i = 0; i <= l; i++) f[i] = INF;

            priority_queue<Node> pq;

            for(int i = A; i <= B; i += 2) {
                if(!cowThere[i]) {
                    f[i] = 1;
                    if(i <= B+2-A) {
                        pq.push(Node(i, 1));
                        //printf("push %d\n", i);
                    }
                }
            }   //动规边界初始化

            for(int i = B + 2; i <= l; i += 2) {
                if(!cowThere[i]) {
                    //cout << "#" << endl;
                    Node u;
                    while(!pq.empty()) {    //这个循环会筛选出求i是需要的范围内的点
                        u = pq.top();
                        //cout << u.x << endl;
                        if(u.x < i-B)   pq.pop();
                        else            break;
                    }

                    if(!pq.empty()) {
                        f[i] = u.fx + 1;
                    }
                }
                if(f[i-A+2] != INF)
                    pq.push(Node(i-A+2, f[i-A+2])); //将下一个点压入,为进一步求解做准备
            }

            if(f[l] == INF) printf("-1\n");
            else            printf("%d\n", f[l]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值