题意:
给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
要求使用差分约束系统的解法解决这道题
输入要求:
输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 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
思路:
差分:
对于差分约束中的每一个不等式约束Xi-Xj<=Ck都可以移项变形为 Xi<=Xj+Ck
如果令Ck=𝑤(𝑖,𝑗),𝑑𝑖𝑠[i]=Xi,𝑑𝑖𝑠[𝑗]=Xj,那么原式变为𝑑𝑖𝑠[𝑖]≤ 𝑑𝑖𝑠[𝑗]+𝑤(𝑖,𝑗),与最短路问题中的松弛操作相似
差分约束问题最后实际上变成最短路问题
这里有两点需要注意:
1)可能出现负环问题,所以使用SPFA
2)最后求得的答案都是界值,跑最短路得到的是一组最大解(上界),如果想要求最小解(下界)则将≤变成≥然后跑最长路即可
具体实现:
1)先要构造Xi<=Xj+Ck的算是形式,这道题给的区间中点的个数, 记dis[𝑖]表示数轴上[0,𝑖]之间选点的个数 , 对于第i个区间[ai,bi]需要满足dis[bi]−dis[ai−1]≥ci
下面就可以构造图了,区间中点的个数即成图后边上的权值
2)求选点的最小值(下界),转化为≥不等式组跑最长路
注意点!!使用了上周的SPFA
这里有个约束条件:需要保证dis是有意义的 即有0≤dis[i]-dis[i-1]≤1
总结:
这道题用查分解的关键是能够得到符合差分使用条件的关系,即dis[bi]−dis[ai−1]≥ci,这一步可能比较难想
代码:
#include<cstdio>
#include<queue>
using namespace std;
const int inf=5*1e8;
int a[50010],dis[50010],cnt[50010],vis[50010]; //记录最长路径,当前最长路的边数,点是否遍历过
int pre[50010],fvis[50010]; //记录是否属于负环
queue<int> q;
int n,tot;
struct edge{
int v,nxt,w;
}e[200010];
int head[50010];
void add(int a,int b,int c){ //加边
e[++tot].v=b; e[tot].nxt=head[a]; e[tot].w=c;
head[a]=tot;
}
void init(){
for(int i=1;i<50010;i++){
dis[i]=-inf; cnt[i]=0; head[i]=0;
vis[i]=0; fvis[i]=0; pre[i]=0;
}
tot=0;
}
void dfs(int s){
fvis[s]=1; //标记属于负环
for(int i=head[s];i!=0;i=e[i].nxt){
if(fvis[e[i].v]!=1)
dfs(e[i].v);
}
}
void SPFA(int s){
dis[s]=0; vis[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front(); q.pop();
vis[u]=0; /
if(fvis[u]) continue; //已经属于负环
for(int i=head[u];i!=0;i=e[i].nxt){
int v=e[i].v;
if(dis[v]<dis[u]+e[i].w){ //需要松弛 //跑最大树
cnt[v]=cnt[u]+1; //更新最短边边数
if(cnt[v]>=50000) //负环
dfs(v); //找到能到的点
dis[v]=dis[u]+e[i].w; //更新最短路长度
pre[v]=u;
if(!vis[v]){
q.push(v); //未到达过,扔到队列
vis[v]=1;
}
}
}
}
}
int main(){
int a,b,c,b_max=0;
scanf("%d",&n);
init();
for(int i=0;i<n;i++){
scanf("%d%d%d",&a,&b,&c);
add(a,b+1,c); //整体右移,不使用a-1,使用b+1
if(b_max<b+1) b_max=b+1; //记录b的最大值
}
for(int i=1;i<=b_max;i++){ //保证dis有意义 0≤dis[i]-dis[i-1]≤1
add(i-1,i,0);
add(i,i-1,-1);
}
SPFA(0);
printf("%d\n",dis[b_max]);
return 0;
}