poj 3171 dp+线段树(最小代价覆盖全部区间)

题意:给定一个需要覆盖的区间[m,e],给出n个区间[ai,bi],每个区间有一个权值wi。求能够把[m,e]覆盖所选取的区间的最小代价之和。

思路:如果没有权值,那么贪心可以解决这个问题,见poj2376。但是此处带权,那么贪心不成立,需要考虑用动态规划。先将所有区间的代价设置为无限大,然后将给的n区间按左优先排序,然后进行一趟遍历:第i个区间为[a,b],先查询[a-1,b-1]区间的最小值min,然后更新[a,b]的最小值为Ci+min(表示从m到b都覆盖到了的最小代价)。最后找到[E,E]区间的最小值就是要覆盖[M,E]区间的最小代价了,若等于无限大值则说明中间有段区间无法覆盖。其中涉及查询区间最小值的操作,所以考虑用线段树来求。

采用线段树的时候还是记得设置一个临时变量,在查询的时候再向下带。不要每次都更新到叶子。其中num是区间内真正的最小值,而flag则是没有往下带的最小值。

线段树的num域充当了动态规划的dp数组。相当于dp的思想+线段树的实现。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
#define INF 0x3ffffffffffffff
#define clr(s,t) memset(s,t,sizeof(s))
#define N 10005
#define M 86405
struct node{
    int a,b,w;
    bool operator<(const node &y)const{
        return y.a>a;
    }
}p[N];
struct tree{
    int l,r;
    long long flag,num;
}t[M<<2];
int n,m,e;
int mid(int x,int y){
    return (x+y)>>1;
}
void create(int r,int a,int b){
    t[r].l = a;
    t[r].r = b;
    t[r].num = t[r].flag = INF;
    if(a == b)
        return;
    create(r*2, a, mid(a,b));
    create(r*2+1, mid(a,b)+1, b);
}
void update(int r,int a,int b,long long x){
    int mm = mid(t[r].l,t[r].r);
    t[r].num = min(t[r].num ,x);
    if(t[r].l ==a && b == t[r].r){
        t[r].flag = min(t[r].flag,x);
        return;
    }
    if(b <= mm)
        update(r*2, a, b, x);
    else if(a>mm)
        update(r*2+1, a, b, x);
    else{
        update(r*2, a, mm, x);
        update(r*2+1, mm+1, b, x);
    }
}
long long querymin(int r,int a,int b){
    int mm = mid(t[r].l,t[r].r);
    if(t[r].l==a && t[r].r==b)
        return t[r].num;
    update(r*2, t[r].l, mm, t[r].flag);
    update(r*2+1, mm+1, t[r].r, t[r].flag);
    if(b<=mm)
        return querymin(r*2, a, b);
    else if(a>mm)
        return querymin(r*2+1, a, b);
    else
        return min(querymin(r*2, a, mm),querymin(r*2+1, mm+1, b));
}
int main(){
    int i;
    long long now;
    scanf("%d %d %d",&n,&m,&e);
    for(i = 0;i<n;i++)
        scanf("%d %d %d",&p[i].a,&p[i].b,&p[i].w);
    sort(p,p+n);
    if(p[0].a > m){
        printf("-1\n");
        return 0;
    }
    create(1,m,e);
    for(i = 0;i<n&&p[i].a==m;i++)
        update(1,m,p[i].b,p[i].w);
    for(;i<n;i++){
        now = querymin(1,p[i].a-1,p[i].b-1);
        update(1,p[i].a,p[i].b,now+p[i].w);
    }
    now = querymin(1, e, e);
    if(now == INF)
        printf("-1\n");
    else
        printf("%lld\n",now);
    return 0;
}


阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页