题目
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点。
Input
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b
表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1
<= ci <= bi - ai+1。
Output
输出一个整数表示最少选取的点的个数
Sample Input5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
题目思路
本题我们可以将它抽象成图,利用图的解法来求解。那么如何抽象成图呢?
首先考虑一个坐标轴1-n,对于i处,此时1到i+1之间的距离是i,此时引入一条边(1,i),对应权值为b,且b<i,那么此时1到i+1之间的距离就被缩短到了b。
现在考虑这个题,我们要求区间(r,l)中选取的最少点,那么可以通过spfa找最长路解决。
对于一个小区间[a,b],如果dis[b+1]<dis[a]+w(a,b)
那么更新dis[b+1]=dis[a]+w(a,b)
实现过程:
构造图,首先构造基础图即坐标轴相邻的两个点之间的距离
加边,将题目给的边加入图中
利用spfa找最长路
输出dis[r](大区间(r,l))
易错点:
这个题交上去后一直re,后来发现是edges数组开小了,没有考虑到基础图加边,add(i,i+1,0) add(i+1,i,-1),以后开数组一定要注意数组的大小
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=50005;
int head[maxn],dis[maxn],inq[maxn];
int vis[maxn],cnt[maxn];
queue<int> q;
int n,tot,r=0,l=inf;
struct node
{
int to,nex,w;
}edges[500010];
void add(int x,int y,int w)
{
tot++;
edges[tot].to=y;
edges[tot].w=w;
edges[tot].nex=head[x];
head[x]=tot;
}
void dfs(int s)
{
vis[s]=1;
for(int i=head[s];i!=-1;i=edges[i].nex)
{
if(vis[edges[i].to]==0)
dfs(edges[i].to);
}
}
void spfa(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,-inf,sizeof(dis));
memset(cnt,0,sizeof(cnt));
memset(inq,0,sizeof(inq));
while(!q.empty()) q.pop();
q.push(s);
dis[s]=0;
inq[s]=1;
while(!q.empty())
{
int x=q.front();q.pop();
inq[x]=0;
for(int i=head[x];i!=-1;i=edges[i].nex)
{
int y=edges[i].to;
if(vis[y]==1) continue;
if(dis[y]<dis[x]+edges[i].w)
{
cnt[y]=cnt[x]+1;
if(cnt[y]>n)
dfs(y);
dis[y]=dis[x]+edges[i].w;
if(inq[y]==0)
{
q.push(y);
inq[y]=1;
}
}
}
}
return;
}
int main()
{
//freopen("a.txt","r",stdin);
memset(head,-1,sizeof(head));
cin>>n;
for(int i=0;i<n;i++)
{
int u,v,z;
cin>>u>>v>>z;
add(u,v+1,z);
r=max(v+1,r);
l=min(u,l);
}
for(int i=l;i<r;i++){
add(i,i+1,0);
add(i+1,i,-1);
}
spfa(l);
printf("%d\n",dis[r]);
return 0;
}