POJ-1201 Intervals---差分约束

题目链接:

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\n", -d[mn - 1]);
85     }
86     return 0;
87 }

 

posted @ 2018-04-07 20:46  _努力努力再努力x  阅读(179)  评论(0编辑  收藏  举报