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是边上的权,如图

Intervals(差分约束)

由题我们可以得知,这个有向图中,由题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,并且需要判断是否有负环,有的话就不存在

 

你可能感兴趣的:(差分约束)