Intervals(差分约束)

http://poj.org/problem?id=1201

题意:给出N个整数区间[ai,bi],并且给出一个约束ci,( 1<= ci <= bi-ai+1),使得数组Z在区间[ai,bj]的个数>= ci个,求出数组Z的最小长度。

思路:建立差分约束系统。因为这里要求数组长度的最小值,要变为 x-y>=k的标准形式。

设数组 s[j] 表示数组 Z 区间[0,j]里包含的元素个数。所以 s[bi+1] - s[ai] >= ci,注意是 j+1,

隐含条件   0 <= s[i+1]-s[i] <= 1;

故差分约束系统为:

s[bi+1] - s[ai] >= ci;

s[i+1] - s[i] >= 0;

s[i] - s[i+1] >= -1;

然后邻接表建图求最长路。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #include<stack>
 5 using namespace std;
 6 
 7 const int maxn = 500100;
 8 const int INF = 0x3f3f3f3f;
 9 struct node
10 {
11     int u,v,w;
12     int next;
13 }edge[maxn];
14 
15 int n,p[maxn],cnt;
16 int Min,Max;
17 int dis[maxn],instack[maxn],vexcnt[maxn];
18 
19 void add(int u, int v, int w)
20 {
21     cnt++;
22     edge[cnt].u = u;
23     edge[cnt].v = v;
24     edge[cnt].w = w;
25     edge[cnt].next = p[u];
26     p[u] = cnt;
27 }
28 
29 bool SPFA()
30 {
31     stack<int>st;
32     while(!st.empty()) st.pop();
33     memset(instack,0,sizeof(instack));
34     memset(vexcnt,0,sizeof(vexcnt));
35     for(int i = Min; i <= Max; i++)
36         dis[i] = -INF;
37 
38     st.push(Min);
39     dis[Min] = 0;
40     instack[Min] = 1;
41     vexcnt[Min]++;
42 
43     while(!st.empty())
44     {
45         int u = st.top();
46         st.pop();
47         instack[u] = 0;
48 
49         for(int i = p[u]; i; i = edge[i].next)
50         {
51             if(dis[edge[i].v] < dis[u] + edge[i].w)
52             {
53                 dis[edge[i].v] = dis[u] + edge[i].w;
54                 if(!instack[edge[i].v])
55                 {
56                     instack[edge[i].v] = 1;
57                     st.push(edge[i].v);
58                     vexcnt[edge[i].v]++;
59                     if(vexcnt[edge[i].v] > n)
60                         return false;
61                 }
62             }
63         }
64     }
65     return true;
66 }
67 
68 int main()
69 {
70     int u,v,w;
71     scanf("%d",&n);
72 
73     cnt = 0;
74     memset(p,0,sizeof(p));
75     Min = INF,Max = -1;
76 
77     for(int i = 0; i < n; i++)
78     {
79         scanf("%d %d %d",&u,&v,&w);
80         add(u,v+1,w);
81         Min = min(Min,u);
82         Max = max(Max,v+1);
83     }
84     for(int i = Min; i < Max; i++)
85     {
86         add(i,i+1,0);
87         add(i+1,i,-1);
88     }
89     SPFA();
90     printf("%d\n",dis[Max]-dis[Min]);
91     return 0;
92 }
View Code

关于差分约束:

比如给出三个不等式,b-a<=k1,c-b<=k2,c-a<=k3,求出c-a的最大值,我们可以把a,b,c转换成三个点,k1,k2,k3是边上的权,如图

由题我们可以得知,这个有向图中,由题b-a<=k1,c-b<=k2,得出c-a<=k1+k2,因此比较k1+k2和k3的大小,求出最小的就是c-a的最大值了

根据以上的解法,我们可能会猜到求解过程实际就是求从a到c的最短路径,没错的....简单的说就是从a到c沿着某条路径后把所有权值和k求出就是c -a<=k的一个

推广的不等式约束,既然这样,满足题目的肯定是最小的k,也就是从a到c最短距离...

理解了这里之后,想做题还是比较有困难的,因为题目需要变形一下,不能单纯的算..

首先以poj3159为例,这个比较简单,就是给出两个点的最大差,然后让你求1到n的最大差,直接建图后用bellman或者spfa求最短路就可以过了

稍微难点的就是poj1364,因为他给出的不等式不是x-y<=k形式,有时候是大于号,这样需要我们去变形一下,并且给出的还是>,<没有等于,都要变形

再有就是poj1201,他要求出的是最长距离,那就要把形式变换成x-y>=k的标准形式

注意点:

1. 如果要求最大值想办法把每个不等式变为标准x-y<=k的形式,然后建立一条从y到x权值为k的边,变得时候注意x-y<k =>x-y<=k-1

   如果要求最小值的话,变为x-y>=k的标准形式,然后建立一条从y到x的k边,求出最长路径即可

2.如果权值为正,用dj,spfa,bellman都可以,如果为负不能用dj,并且需要判断是否有负环,有的话就不存在

 

posted on 2013-11-24 19:31  straw_berry  阅读(440)  评论(0编辑  收藏  举报