题目描述
一个整数集合Z有n个区间,每个区间有3个值,ai,bi,ci代表,在区间[ai,bi]上至少有ci个整数属于集合Z,ci可以在区间内任意取不重复的点。
现在要满足所有区间的约束条件,问最少可选多少个点。
输入
第一行一个整数n,表示区间个数
以下n行描述区间,第i+1行,三个整数ai,bi,ci,由空格隔开,其中 0 <= ai <= bi <= 50000 且1 <= ci <= bi - ai+1.
输出
一行,输出满足要求序列的长度的最小值
样例输入
5 3 7 3 8 10 3 6 8 1 1 3 1 10 11 1
样例输出
6
提示
这题要注意的点是,我们求的是区间内的最少点数,数组dist[i]表示从头直到第i个点(包括i)中点的数目,每组 ai,bi,ci表示[ai,bi]=ci,即dist[b]-dist[a-1]=ci,但由于a的数据可能取到0,a-1就会小于0,所以我们把所有区间数字增加1,就变成了dist[b+1]-dist[a]=c,然后我们就建一条a->b+1的权值为c的有向边。
对于每一个点i,在初始的情况下都处于可选可不选,即dist[i+1]-dist[i]=0/1,所以每一个i都要建两条边i->i+1权值为0的有向边和i+1->权值为-1的有向边。那么为什么不是建i-i+1权值为1和i+1->i权值为0的两条边呢,这时因为在spfa中,如果建了i-i+1权值为1这条边,在i点可以不选的时候,很有可能因为dist[v]<dist[u]+w(w=1)而选更新这个数组,导致i点被选中,不仅如此,还会直接影响到后面的dist数组,违反了题目求最小的原则。而换成i+1->权值为-1这条边,在i点可以不选的时候,选这条边只会让dist[u]+w减小,dist不会更新,所以这个时候i点就不会被选中。
#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=2e5+10;
const int INF=0x7f7f7f7f;
int n,maxnum=0,tot=0,head[maxn],dist[maxn],vis[maxn],in[maxn];
struct Edge
{
int to,weight,next;
}edge[maxn*2];
void add_edge(int from,int to,int w)
{
edge[++tot].to=to;
edge[tot].next=head[from];
edge[tot].weight=w;
head[from]=tot;
}
void spfa()
{
for(int i=0;i<=maxnum;i++)
dist[i]=-INF;
queue<int>q;
q.push(0);
vis[0]=1;
dist[0]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to,w=edge[i].weight;
if(dist[v]<dist[u]+w)//求最小值跑最长路径
{
dist[v]=dist[u]+w;
if(!vis[v])//如果v没有入队
{
q.push(v);
vis[v]=1;
}
}
}
}
}
int main()
{
scanf("%d",&n);
while(n--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
/*
依照题意:dist[b]-dist[a-1]=c;但是a-1可能会取到-1;
所以将所有数自增,就是dist[b+1]-dist[a]=c;
*/
if(b+1>maxnum)
maxnum=b+1;
add_edge(a,b+1,c);
}
for(int i=0;i<=maxnum;i++)
{
add_edge(i,i+1,0);//dist[i+1]-dist[i]=0/1;i+1点可选可不选
add_edge(i+1,i,-1);
}
spfa();
cout<<dist[maxnum]<<endl;
return 0;
}