POJ1201Intervals(差分约束系统)

    昨天看了下差分约数系统的含义,其实就是如果有n个变量在m个形如aj-ai>=bk条件下,求解的此不等式的方法。

   而这种不等式的解法其实就是转化为图论的最小路的算法求解的。我们将上面的不等式边形后得到aj>=ai+bk正好就可以看做是从ai到aj权值是bk的一条路径最短的边。这样一来,只要依照题目的条件写出一系列这样的不等式,也就是相当于按照题意增加了一些合法的边,也就完全转化为了最短路的算法。

   再看这道题,题目说[ai, bi]区间内和点集Z至少有ci个共同元素,那也就是说如果我用Si表示区间[0,i]区间内至少有多少个元素的话,那么Sbi - Sai >= ci,这样我们就构造出来了一系列边,权值为ci,但是这远远不够,因为有很多点依然没有相连接起来(也就是从起点可能根本就还没有到终点的路线),此时,我们再看看Si的定义,也不难写出0<=Si - Si-1<=1的限制条件,虽然看上去是没有什么意义的条件,但是如果你也把它构造出一系列的边的话,这样从起点到终点的最短路也就顺理成章的出现了。

我们将上面的限制条件写为同意的形式:

Sbi - Sai >= ci

Si - Si-1 >= 0

Si-1 - Si >= -1

这样一来就构造出了三种权值的边,而最短路自然也就没问题了。

但要注意的是,由于查分约束系统里常常会有负权边,所以为了避免负权回路,往往用Bellman-Ford或是SPFA求解(存在负权回路则最短路不存在)。

 1 #include <map>
 2 #include <set>
 3 #include <stack>
 4 #include <queue>
 5 #include <cmath>
 6 #include <ctime>
 7 #include <vector>
 8 #include <cstdio>
 9 #include <cctype>
10 #include <cstring>
11 #include <cstdlib>
12 #include <iostream>
13 #include <algorithm>
14 using namespace std;
15 #define eps 1e-15
16 #define MAXN  50005
17 #define INF 1000000007
18 #define MAX(a,b) (a > b ? a : b)
19 #define MIN(a,b) (a < b ? a : b)
20 #define mem(a) memset(a,0,sizeof(a))
21 
22 struct EDGE//使用邻接表存边
23 {
24     int v;
25     int w;
26     int next;
27 }edge[3*MAXN];
28 int head[MAXN], d[MAXN], vis[MAXN],N, n, Max,Min;
29 
30 void AddEdge(int u,int v,int w)//添加新边
31 {
32     edge[N].v = v;
33     edge[N].w = w;
34     edge[N].next = head[u];
35     head[u] = N++;
36 }
37 
38 void SPFA()//此题数据较大,用Bellman-ford恐怕过不了
39 {
40     for(int i=Min;i<=Max;i++) d[i] = -INF;
41     d[Min] = 0;
42     queue<int>q;
43     q.push(Min);
44     while(!q.empty())
45     {
46         int x = q.front(); q.pop();
47         vis[x] = 0;
48         for(int e=head[x];e != -1;e=edge[e].next)if(d[edge[e].v] < d[x] + edge[e].w)
49         {
50             d[edge[e].v] = d[x] +edge[e].w;
51             if(!vis[edge[e].v])
52             {
53                 q.push(edge[e].v);
54                 vis[edge[e].v] = 1;
55             }
56         }
57     }//由于题目说一定有解,我就略去了判断是否存在负权回路
58 }
59 
60 int main()
61 {
62     while(~scanf("%d", &n))
63     {
64         int u,v,w; N = 0;
65         memset(head,-1,sizeof(head));
66         mem(vis);  mem(edge);
67         Min = INF;  Max = -INF;
68         for(int i=0;i<n;i++)
69         {
70             scanf("%d%d%d", &u,&v,&w);//题目条件的边
71             AddEdge(u,v+1,w);
72             Min = MIN(Min, u);//在这里记录最小值和最大值,所求的就是以Min为源点
73             Max = MAX(Max, v+1);//以Max为终点的最短路
74         }
75         for(int i = Min;i < Max; i++)//添加新边
76         {
77             AddEdge(i,i+1,0);
78             AddEdge(i+1,i,-1);
79         }
80         SPFA();
81         printf("%d\n", d[Max]);
82     }
83     return 0;
84 }

 

posted @ 2013-08-16 11:30  再见~雨泉  阅读(4701)  评论(0编辑  收藏  举报