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 }
关于差分约束:
比如给出三个不等式,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,并且需要判断是否有负环,有的话就不存在