背包问题 [二维偏序, 树状数组]

背包问题


color{red}{正解部分}

二维偏序问题,

将所 有点 按照 vv 为第一关键字, ww 为第二关键字 从大到小 排序,

从前往后扫, 离散化坐标, 使用 树状数组 维护 yy前缀最大值, 记为 max_curmax\_cur

对于背包的限制, 将背包按容量 从大到小 排序, 维护一个指针从左往右根据物品的容量往右移动, 记为 tt,

背包从容量大的开始选显然是最优的 .

min(t,max_cur)min(t, max\_cur) 更新 .


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 = 1e5 + 5;

int N;
int M;
int Len_2;
int rl[maxn];
int B2[maxn];

struct Node{ int w, v; } A[maxn];

bool cmp(Node a, Node b){ return a.w==b.w?a.v>b.v:a.w>b.w; }

struct Bit_Tree{
        int v[maxn], lim;
        void Add(int k, int x){ while(k<=lim)v[k]=std::max(v[k], x), k+=k&-k; }
        int Query(int k){ int s = 0; while(k){ s = std::max(v[k], s); k -= k&-k; } return s; }
} bit_t;

void Work(){
        N = read();
        for(reg int i = 1; i <= N; i ++) A[i].w = read(), B2[i] = A[i].v = read();
        std::sort(A+1, A+N+1, cmp);
        std::sort(B2+1, B2+N+1), Len_2 = std::unique(B2+1, B2+N+1) - B2-1;
        for(reg int i = 1; i <= N; i ++) A[i].v = std::lower_bound(B2+1, B2+Len_2+1, A[i].v)-B2;
        M = read(); 
        for(reg int i = 1; i <= M; i ++) rl[i] = read();
        std::sort(rl+1, rl+M+1, std::greater<int>());
        int t = 0, Ans = 0;
        bit_t.lim = Len_2; memset(bit_t.v, 0, sizeof bit_t.v);
        for(reg int i = 1; i <= N; i ++){
                while(t < M && rl[t+1] >= A[i].w) t ++;
                int p = bit_t.Query(Len_2-A[i].v+1), cur = std::min(t, p+1);
                bit_t.Add(Len_2-A[i].v+1, cur); Ans = std::max(Ans, cur);
        }
        printf("%d
", Ans);
}

int main(){
        int T = read(); while(T --) Work();
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822397.html