题意:给定n个整数的闭区间和n个整数c1,c2,...,cn。编程实现以下三点。
⑴以标准输入方式读入闭区间的个数,每个区间的端点和整数c1,c2,...,cn。
⑵求一个最小的整数集合Z,满足Z在[ai,bi]这个闭区间的个数不小于ci个。
⑶以标准输出方式输出答案,输出Z的个数。
分析:该题目可以建模成一个差分约束系统。以样例输入为例进行分析。
⑴Z集合在范围[ai,bi]的整数个数即,s[bi] - s[ai]至少为ci个,可建立以下不等式组:
s[bi] - s[ai-1] >= ci ---> s[ai-1] - s[bi] <= -ci
s2 - s7 <= -3
s7- s10 <= -3
s5 - s8 <= -1
s0 - s3 <= -1
s9 - s11 <= -1
⑵根据题目条件,还有两个约束条件:
s[i] - s[i-1] <= 1
s[i] - s[i-1] >= 0 ---> s[i-1] - s[i] <= 0
最终要求解的是什么?设所有区间右端点的最大值为mx,区间左端点的最小值为mn,那么最终要求的是s[mx] - s[mn-1]的最小值,即s[mx] - s[mn-1] >= M中的M,转换成s[mn-1] - s[mx] <= -M,即要求源点s[mx]到s[mn]的最短路径。
因为最后两个约束条件很普遍,所以我们不必将边添加到邻接表当中,在程序中添加两个判断即可。
以上参考自:《图论算法理论、实现及应用》 ----北京大学出版社
spfa代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int inf = 1000000000;
const int maxn = 50005;
struct edge{
int v,w;
int next;
edge(){}
};
int n;
int dist[maxn];
bool inq[maxn];
queue<int> q;
int list[maxn];
edge a[maxn];
int mx,mn;
bool input(){
if(scanf("%d",&n) == EOF) return false;
int u,v,w;
for(int i = 0; i < maxn; i++) list[i] = -1;
mx = -inf;
mn = inf;
for(int i = 0; i < n; i++){
scanf("%d%d%d",&v,&u,&w);
mn = min(mn,v);
mx = max(mx,u);
a[i].v = v-1;
a[i].w = -w;
a[i].next = list[u];
list[u] = i;
}
return true;
}
void relax(int u, int v, int w){
if(dist[u] + w < dist[v]){
dist[v] = dist[u] + w;
if(!inq[v]){
q.push(v); inq[v] = true;
}
}
}
void spfa(int s){
for(int i = mn-1; i <= mx; i++){
dist[i] = inf;
inq[i] = false;
}
dist[s] = 0;
q.push(s);
while(!q.empty()){
int u = q.front(); q.pop(); inq[u] = false;
//s[i] - s[i-1] <= 1
if(u+1 < maxn)
relax(u,u+1,1);
//s[i] - s[i-1] >= 0
if(u-1 >= 0)
relax(u,u-1,0);
for(int p = list[u]; p != -1; p = a[p].next){
relax(u,a[p].v,a[p].w);
}
}
}
void solve(){
spfa(mx);
printf("%d\n",-dist[mn-1]);
}
int main(){
while(input()){
solve();
}
return 0;
}