POJ 1201 Intervals (差分约束系统, 贪心+线段树)

题目类型  差分约束系统, 贪心+线段树

题目意思
给出最多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;
}

你可能感兴趣的:(数据结构,图论)