SDU程序设计思维Week8-作业 A-区间选点II

程序设计思维Week8-作业

A-区间选点II

Description

给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点。
使用差分约束系统的解法解决这道题
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
输出一个整数表示最少选取的点的个数

Sample

Input:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

Output:
6

Idea

前提:使用差分约束解决问题,根据不等式组形成图,用前向星存储图

  • 构造不等式组
    记sum[i]表示数轴上[0,i]之间选点的个数。对于第i个区间[ai,bi]需要满足sum[b+1]-sum[a]>=C,即sum[b+1]>=C+sum[a],a指向b+1的边权重为C,每次存储边,同时更新最左边界和最右边界
    需要约束单位区间至多选一个点,即0<=sum[i+1]-sum[i]<=1,i指向i+1的边权重为0,i+1指向i的边权重为-1,从最左边界至最右边界,对每个单位区间添加这两条边
  • 由题得到的不等式组都是>=,求该差分约束系统的最小解,跑最长路
    使用SPFA求解从最左边界出发到达最右边界的最长路
  • SPFA遍历后,获得dis[最右边界]即总共最少要选的点

Summary

区间选点I的方法是按右端点排序后贪心
区间选点II的方法是差分约束系统构建图+SPFA
图中有负权边,利用SPFA求解最长路径只需改变一个判断条件。
存边时可以将[a,b]转化成[a,b+1),权重是每个区间至少需要的点个数。记录最左边界和最右边界,以确定SPFA的起点以及终点对应的dis。约束每个单位区间至多选一个点。
T了几次,原因是遍历前向星的边时陷入死循环,应该是中断条件不对,改成头指针都初始化为0可以解决。
时间复杂度平均为O(km)

Codes

#include <iostream>
#include <queue>
using namespace std;
const int inf = 1e10;
int n,sum[50020],dis[50020],vis[50020],inq[50020],head[50020],tot=0, bmax=-1,amin=inf;
struct edge {
	int u,v,w, next;

}e[200020];

void add(int u,int v,int w) {
	e[++tot].u = u;
	e[tot].v = v;
	e[tot].w = w;
	e[tot].next = head[u];
	head[u] = tot;

}

void init() {
	tot = 0;
	for (int i = 0; i <= n; i++) {
		dis[i] = -inf;
		vis[i] = 0;
		head[i] = 0;

	}
}
void spfa(int s) {
	queue<int> q;
	for (int i = 0; i <= n; ++i)
		dis[i] = -inf, vis[i] = 0;

	q.push(s);
	dis[s] = 0;
	vis[s] = 1;

	while (!q.empty()) {
		int u = q.front(); q.pop();
		vis[u] = 0;
		for (int i = head[u]; i != 0; i = e[i].next) {
			//cout << "k";
			int v = e[i].v,w=e[i].w;
			if (dis[v] < dis[u] + w) {
				dis[v] = dis[u] + w;
				if (!vis[v]) {
					q.push(v);
					vis[v] = 1;
				}
			}
			//if (t.next == -1)break;
		}
	}
}
int main()
{
	cin.sync_with_stdio(false);
	cin >> n;
	init();
	
	//sum[0] = 0;
	for (int i = 1; i <= n; i++) {
		int a, b,c;
		cin >> a >> b >> c;
		//scanf("%d%d%d", &a, &b, &c);
		add(a, b+1, c);
		if (bmax < b+1)bmax = b+1;
		if (amin > a)amin = a;
	}
	for (int i = amin; i < bmax; i++) {
		add(i, i+1, 0);
		add(i+1, i , -1);
	}
	spfa(amin);
	cout << dis[bmax] << endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值