区间 [动态规划]

区 间 区间


正 解 部 分 \color{red}{正解部分}

首先 一个点最多被两个区间覆盖,

若按常规套路设 F [ i ] F[i] F[i] 表示处理到前 i i i 个区间覆盖 [ 1 , r i ] [1, r_i] [1,ri] 的最小代价,
转移: F [ i ] = min ⁡ ( F [ i ] , max ⁡ ( F [ k ] ,   w i + [ r k ≠ l i − 1 ] × w k ) ) F[i] = \min(F[i], \max(F[k],\ w_i +[r_k\not =l_i-1]\times w_k)) F[i]=min(F[i],max(F[k], wi+[rk=li1]×wk))

如下图, F [ i ] F[i] F[i] 如果从 F [ k ] F[k] F[k] 转移过来, 会 “忽略” j j j 的存在, 导致答案变为 8 8 8,

在转移时并不知道转移的来源会与哪些区间重合, 导致并不知道这个区间会与哪些区间重合,
换就话说, 转移的来源并不纯净,
我们需要转移的时候使得两个区间相交的区域没有别的区间存在, 于是可以想到多设一维去保证上述条件 .

F [ i , j ] F[i, j] F[i,j] 表示处理到第 i i i 个区间, ( j , r i ] (j, r_i] (j,ri] 被覆盖一次的 最大值,
假设从第 k k k 个区间转移而来, 前提是 r k ≥ l i − 1 r_k \geq l_i-1 rkli1, 分情况讨论,

  • r k = l i − 1 r_k = l_i-1 rk=li1, F [ i , r k ] = min ⁡ ( F [ i , r k ] ,   max ⁡ ( F [ k , 1   . . .   l i − 1 ] , w i ) ) F[i, r_k] = \min\left(F[i, r_k],\ \max(F[k, 1\ ...\ l_i-1], w_i)\right) F[i,rk]=min(F[i,rk], max(F[k,1 ... li1],wi))
  • r k ≥ l i r_k \geq l_i rkli, F [ i , r k ] = min ⁡ ( F [ i , r k ] ,   max ⁡ ( F [ k , 1   . . .   l i − 1 ] , w i + w k ) ) F[i, r_k] = \min\left(F[i, r_k], \ \max(F[k, 1\ ... \ l_i-1], w_i+w_k)\right) F[i,rk]=min(F[i,rk], max(F[k,1 ... li1],wi+wk))

实 现 部 分 \color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 3005;
const int inf = 0x3f3f3f3f;

int N;
int M;
int F[maxn][maxn];

struct Intval{ int l, r, w; } A[maxn];

bool cmp(Intval a, Intval b){ return a.r < b.r; }

void Work(){
        N = read(), M = read();
        for(reg int i = 1; i <= N; i ++) A[i].l = read(), A[i].r = read(), A[i].w = read();
        std::sort(A+1, A+N+1, cmp);
        for(reg int i = 1; i <= N; i ++){
                for(reg int j = 0; j <= M; j ++) F[i][j] = inf;
                for(reg int j = i; j >= 0; j --){
                        if(A[j].r < A[i].l-1) break ;
                        F[i][A[j].r] = std::min(F[i][A[j].r], std::max(F[j][A[i].l-1], (A[j].r==A[i].l-1?0:A[j].w) + A[i].w));
                }
                for(reg int j = 1; j <= M; j ++) F[i][j] = std::min(F[i][j], F[i][j-1]);
        }
        int Ans = inf;
        for(reg int i = N; i >= 1; i --){ if(A[i].r < M) break ; Ans = std::min(Ans, F[i][A[i].r]); }
        printf("%d\n", Ans==inf?-1:Ans);
}

int main(){ int T = read(); while(T --) Work(); return 0; }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值