题目类型 差分约束系统, 贪心+线段树
题目意思
给出最多50000个整数区间以及要求这些区间至少包含的整数的个数
现在需要找一个元素数量最小的整数集合满足以上条件 问最少的元素个数是多少
解题方法
方法一 差分约束系统
d[i] 表示从 0 -> i-1 这个区间包含的整数的个数
那么其中的一个条件 l, r, c 则表示 d[r+1] - d[l] >= c
还有额外的条件 0 <= d[i]-d[i-1] <= 1 即 d[i] - d[i-1] >= 0, d[i-1] - d[i] >= -1 具体建图看代码
方法二 贪心+线段树
先把输入的条件按右端点从小到大排序
然后当处理某一条件时先查询 l -> r 之间已经有多少个数在构造的集合中了 如果这个数量比要求的数量 要大或相等的话就不用处理了
如果小的话 就按 r -> l 的顺序把没有选的整数给选上 (当然选的数量要减去 l -> r 之间前面已经选的数的数量) 即尽量从右端往左端选数更好
因为这样能使后面选的数尽量少 这里的查询和更新需要用到线段树优化时间 具体看代码
参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
方法一代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 1<<29;
const int maxn = 50000 + 10;
int d[maxn], L, R;
bool vis[maxn];
vector<int>E[maxn];
vector<int>W[maxn];
void spfa() {
memset(vis, 0, sizeof(vis));
for( int i=L; i<=R+1; i++ ) d[i] = -INF;
d[R+1] = 0;
vis[R+1] = 1;
queue<int>q;
q.push(R+1);
while(!q.empty()) {
int f = q.front(); q.pop(); vis[f] = false;
//printf("f = %d\n", f);
for( int i=0; i<E[f].size(); i++ ) {
int v = E[f][i], w = W[f][i];
if(d[v] < d[f] + w) {
d[v] = d[f] + w;
//printf("v = %d d[v] = %d\n", v, d[v]);
if(vis[v] == false) {
vis[v] = true;
q.push(v);
}
}
}
if(f < R && d[f] > d[f+1]) { // 以下为额外条件
d[f+1] = d[f];
if(vis[f+1] == false) {
vis[f+1] = true;
q.push(f+1);
}
}
if(f > L && d[f] - 1 > d[f-1]) {
d[f-1] = d[f] - 1;
if(vis[f-1] == false) {
vis[f-1] = true;
q.push(f-1);
}
}
}
}
int main() {
freopen("in", "r", stdin);
int n;
while(scanf("%d", &n) != EOF) {
L = INF, R = -INF;
for( int i=0; i<=maxn+2; i++ ) E[i].clear(), W[i].clear();
for( int i=0; i<n; i++ ) {
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
//E[l] d[r+1] - d[l] >= c 题目给出的条件
E[l].push_back(r+1);
W[l].push_back(c);
L = min(L, l);
R = max(R, r+1);
}
int rt = R + 1;
for( int i=L; i<=R; i++ ) {
/*if(i != R) {
E[i].push_back(i+1);
W[i].push_back(0);
}
if(i != L) {
E[i].push_back(i-1);
W[i].push_back(-1);
}*/ // 额外条件写在 spfa 中使所用时间更短
E[rt].push_back(i);
W[rt].push_back(0);
}
spfa();
printf("%d\n", d[R]);
}
return 0;
}
方法二代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mid ((l+r)>>1)
const int INF = 1<<29;
const int maxn = 50000 + 10;
int N[maxn*4];
bool all[maxn*4];
struct Node {
int l, r, c;
bool operator < (const Node & rhs) const {
if(r == rhs.r) return c > rhs.c;
else return r < rhs.r;
}
}a[maxn];
void pushdown(int rt, int l, int r) {
if(l == r) return ;
if(all[rt]) {
N[ls] = mid-l+1;
N[rs] = r - mid;
all[ls] = all[rs] = true;
all[rt] = false;
}
}
int query(int rt, int l, int r, int L, int R) {
pushdown(rt, l, r);
if(l == L && r == R) return N[rt];
if(R <= mid) return query(ls, l, mid, L, R);
else if(L > mid) return query(rs, mid + 1, r, L, R);
else return query(ls, l, mid, L, mid) + query(rs, mid + 1, r, mid + 1, R);
N[rt] = N[ls] + N[rs];
}
void update(int rt, int l, int r, int x, int c) {
pushdown(rt, l, r);
//printf("%d %d %d x = %d c = %d\n", rt, l, r,x, c);
if(r-l+1-N[rt] == c) {
N[rt] = r-l+1;
all[rt] = true;
return ;
}
if(x <= mid) update(ls, l, mid, x, c);
else {
int tn = query(1, 0, maxn-10, mid+1, min(r,x));
tn = min(r,x)-mid-tn;
//printf("tn = %d\n", tn);
if(tn < c) {
update(ls, l, mid, x, c - tn);
update(rs, mid + 1, r, x, tn);
}
else update(rs, mid + 1, r, x, c);
}
N[rt] = N[ls] + N[rs];
}
int main() {
freopen("in", "r", stdin);
int n;
while(scanf("%d", &n) != EOF) {
memset(N, 0, sizeof(N));
memset(all, 0, sizeof(all));
for( int i=0; i<n; i++ ) {
int l, r, c;
scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].c);
}
sort(a, a + n);
for( int i=0; i<n; i++ ) {
int num = query(1, 0, maxn-10, a[i].l, a[i].r);
if(num >= a[i].c) continue;
a[i].c -= num;
update(1, 0, maxn-10, a[i].r, a[i].c);
}
printf("%d\n", N[1]);
}
return 0;
}