A - 区间选点 II
题目描述
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
Input
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
Output
输出一个整数表示最少选取的点的个数
Sample Input
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
Sample Output
6
题解
本题使用差分约束的方法解决,将差分约束中的不等式抽象成图。
结构体数组e用来存储所有边,int型数组head用来存储前向星链表,int型数组sum用来存储[0,i-1]之间选点的个数,vis数组记录该点是否位于队列中(防止重复进入队列)。mn和mx记录所有的点中的最小值和最大值(实际记录的是最大值+1)。
输入的a,b,c表示sum[b+1]-sum[a]>=c。因为0<=sum[i+1]-sum[i]<=1,所以sum[i+1]-sum[i]>=0,sum[i]-sum[i+1]>=-1。将这些不等式组都作为图中的边。这些不等式组都是>=,求该差分约束系统的最小解,跑最长路。
spfa遍历后,得到的sum[mx]即为最后结果。
易错点:一开始初始化的sum数组中元素都为0,这样导致了一些元素没有被遍历到(如:sum[2]和sum[4]都进过队列,但是在遍历sum[2](为0)和sum[4](为0或为1)时,检查到sum[3](也为0)的时候就不会再把3放进队列,这时如果有从3到8的约束条件就没有用上,就会出错),所以应该初始化的sum数组中元素都为-1。
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
int mn=100000,mx=-1;//最左边的点和最右边的点
int head[50100];//前向星链表
int sum[50100];// sum[i]表示[0,i-1]之间选点的个数
int vis[50100];
struct edge{
int to,nxt,w;
}e[200000];
int tot=0;
void addEdge(int x,int y,int w){
//cout<<tot<<":"<<x<<" "<<y<<" "<<w<<endl;
e[tot].nxt=head[x];
e[tot].to=y;
e[tot].w=w;
head[x]=tot;
tot++;
}
void init(){
memset(head,-1,sizeof(head));
memset(sum,-1,sizeof(sum));//注意
memset(vis,0,sizeof(vis));
}
void spfa(int s){
queue<int>q;
q.push(s);
sum[s]=0;vis[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
vis[x]=0;
for(int i=head[x];i!=-1;i=e[i].nxt){
int y=e[i].to,w=e[i].w;
if(sum[y]<sum[x]+w){
sum[y]=sum[x]+w;
if(vis[y]==0){
q.push(y);
vis[y]=1;
}
}
}
}
}
int main(int argc, char** argv) {
init();
int n;scanf("%d",&n);
while(n--){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
addEdge(a,b+1,c);
if(a<mn){mn=a;}
if(b+1>mx){mx=b+1;}
}
for(int i=mn;i<mx;i++){
addEdge(i,i+1,0);
addEdge(i+1,i,-1);
}
spfa(mn);
printf("%d\n",sum[mx]);
return 0;
}