题目链接:
https://vjudge.net/problem/POJ-1201
题目大意:
一个整数集合Z有n个区间,每个区间有3个值,ai,bi,ci代表,在区间[ai,bi]上至少有ci个整数属于集合Z,ci可以在区间内任意取不重复的点。
现在要满足所有区间的约束条件,问最少可选多少个点。
Sample Input
5 3 7 3 8 10 3 6 8 1 1 3 1 10 11 1
Sample Output
6
思路:
求最小的整数集合Z满足| Z ∩ [ ai, bi ] | >= ci。意思就是在区间[ai,bi]上至少有ci个整数属于集合Z。
可以建模成一个差分约束系统。以输入的测试数据为例进行分析:
设S[i]是集合Z中小于等于i的元素个数。那么就有以下不等式组:
(1)Z集合中范围在[ai, bi]的整数个数至少为ci,也就相当于S[bi] - S[ai - 1] >= ci
根据差分约束系统中的三角不等式(d[v] - d[u] <= Edge[u][v])
上述不等式可以转化成S[ai - 1] - S[bi] <= -ci 边:<bi, ai-1> 权值-ci
上述例子就有以下不等式组(1)
S2 - S7 <= -3
S7 - S10 <= -3
S5 - S8 <= -1
S0 - S3 <= -1
S9 - S11 <= -1
(2)根据实际情况,还有两个约束条件
S[i] - S[i - 1] <= 1
S[i] - S[i - 1] >= 0,即S[i - 1] - S[i] <= 0
最终要求的是什么?
设所有区间右端点的最大值为mx,如该测试数据中mx = 11, 所有区间左端点的最小值为mn
如该测试数据中mn = 1, mn - 1 = 0
最终要求的是S[mx] - S[mn - 1]的最小值,也就是S[mx] - S[mn - 1] >= M(M的最大值)
也就是S[mn - 1] - S[mx] <= -M(-M的最小值), 也就是点mx到点mn-1的最短路-M,答案求的是M
这里需要注意的是源点是mx,终点是mn-1。
所以进行bellman算法时,应该从mx出发,求出dist[mn - 1]。(dist数组保存的是从源点出发到各点的最短距离)
但是上述的约束条件太多,最多会达到3*50000条边,全部转化成边不是个好方法,所以应该进行优化:
(1)用上述不等式组(1)进行建图,源点到各个顶点最短路径初始为0,因为Si - Smx <=0,所以源点到各个顶点的距离一定会小于等于0,
(2)用Bellman算法求出源点到各个顶点的最短路径长度,每次循环时,判断完约束条件(1)后,再判断约束条件(2)和(3)
约束条件(2)的判断
S[i] - S[i - 1] <= 1等效于S[i] - S[mx] <= S[i - 1] - S[mx] + 1
由于上述dist数组表示从源点出发到各点的最短距离,所以S[i] - S[mx]就是dist[i]
上述条件转化成了dist[i] <= dist[i - 1] + 1
如果dist[i] > dist[i - 1] + 1, 那么修改dist[i] = dist[i - 1] + 1。
约束条件(3)的判断
S[i - 1] <= S[i] 等效于S[i - 1] - S[mx] <= S[i] - S[mx]
也就是dist[i - 1] <= dist[i]
如果dist[i - 1] > dist[i] 那就修改dist[i- 1] = dist[i]
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #include<stack> 8 #include<map> 9 #include<set> 10 #include<sstream> 11 #define MEM(a, b) memset(a, b, sizeof(a)); 12 using namespace std; 13 typedef long long ll; 14 const int maxn = 50000 + 10; 15 const int INF = 0x3f3f3f3f; 16 int T, n, m, cases, tot; 17 int d[maxn]; 18 struct edge 19 { 20 int u, v, w; 21 edge(){} 22 edge(int u, int v, int w):u(u), v(v), w(w){} 23 }; 24 edge a[maxn]; 25 int mx, mn;//mn左区间最小值,mx右区间最大值 26 void init() 27 { 28 tot = 0; 29 MEM(d, 0)//初始化源点0到各个顶点的距离,这里的d[mx]永远是0,这就是源点 30 mx = 1, mn = INF; 31 } 32 void bellman() 33 { 34 bool update = 1; 35 while(update)//如果有不更新的时候,直接退出 36 { 37 update = 0; 38 for(int i = 0; i < n; i++)//Bellman算法的边循环 39 { 40 int u = a[i].u, v = a[i].v, w = a[i].w; 41 if(d[v] > d[u] + w) 42 { 43 update = 1; 44 d[v] = d[u] + w; 45 } 46 } 47 48 //约束条件2的判断 49 for(int i = mn; i <= mx; i++) 50 { 51 if(d[i] > d[i - 1] + 1) 52 { 53 update = 1; 54 d[i] = d[i - 1] + 1; 55 } 56 } 57 58 //约束条件3的判断 59 for(int i = mx; i >= mn; i--) 60 { 61 if(d[i - 1] > d[i]) 62 { 63 update = 1; 64 d[i - 1] = d[i]; 65 } 66 } 67 } 68 } 69 int main() 70 { 71 while(cin >> n) 72 { 73 init(); 74 int u, v, w; 75 for(int i = 0; i < n; i++) 76 { 77 scanf("%d%d%d", &u, &v, &w); 78 a[i] = edge(v, u - 1, -w);//建边 79 mx = max(mx, v); 80 mn = min(mn, u); 81 } 82 //cout<<mx<<" "<<mn<<endl; 83 bellman(); 84 printf("%d ", -d[mn - 1]); 85 } 86 return 0; 87 }