差分约束问题:
如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
求解差分约束系统,可以转化成图论的
单源最短路径(或最长路径)问题。
观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边(i,j),边权为bk。我们再增加一个源点s,s与所有定点相连,边权均为0。对这个图,以s为源点运行Bellman-ford算法(或SPFA算法),最终{d[ i]}即为一组可行解。
令前
k个盒子放的数目为
,则有
,
(1 ≤
i ≤
n ),
(1 ≤
i ≤
m)。以此约束条件用如上算法给出最短路径序列
,由定理知
是合法的放法,且
为最大值。
差分约束系统的确立要根据自己确定的约束条件,从约束点走向被约束点
连边一般有两种方法,第一种是连边后求最长路的方法,第二种是连边后求最短路的方法。
Poj 1201
http://poj.org/problem?id=1201
Intervals
Description
You are given n closed, integer intervals [ai, bi] and n integers c1, ..., cn.
Write a program that: reads the number of intervals, their end points and integers c1, ..., cn from the standard input, computes the minimal size of a set Z of integers which has at least ci common elements with interval [ai, bi], for each i=1,2,...,n, writes the answer to the standard output. Input
The first line of the input contains an integer n (1 <= n <= 50000) -- the number of intervals. The following n lines describe the intervals. The (i+1)-th line of the input contains three integers ai, bi and ci separated by single spaces and such that 0 <= ai <= bi <= 50000 and 1 <= ci <= bi - ai+1.
Output
The output contains exactly one integer equal to the minimal size of set Z sharing at least ci elements with interval [ai, bi], for each i=1,2,...,n.
Sample Input 5 3 7 3 8 10 3 6 8 1 1 3 1 10 11 1 Sample Output 6 Source |
题目意思:
有n个[ai,bi]区间,以及对应一个ci值。求最小的集合Z,满足与[ai,bi]区间有ci个相同元素。
构建差分约束:dist[i]表示源点s到i的区间[s,i]与Z集合相同元素个数,那么满足:
dist[i]<=dist[i+1]<=dist[i]+1
&&
dist[ai]+ci<=dist[bi+1]
(bi+1的原因是区间个数需要加1)
根据最短路不等式,若(a,b,c),即a到b有边权为c,那么dist[b]<=dist[a]+c
(a约束点,b被约束点)
如此,建求最短路图:(i+1,i,0),(i,i+1,1) && (bi+1,ai,-ci) 结果为最大点(约束点)到最小点(被约束点)的最短路。结果是负值需要取反。
建求最远路图:最远不等式,若(a,b,c),那么dist[b]>=dist[a]+c
建图:(i,i+1,0),(i+1,i,-1) && (ai,bi+1,ci)
结果为最小点(约束点)到最大点(被约束点)的最远路。结果为正值。
//最短路求法,dist初始INF
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3fffffff
struct node{ //容器做边表
int v,w;
node(int v_=0,int w_=0):v(v_),w(w_){}
};
vector <node> edge[50001];
int mark[50001];//spfa标记数组
int dist[50001];//距离数组
void spfa(int s,int e){//s到e的spfa
queue <int> que;
que.push(s);
mark[s]=1;
dist[s]=0;
while(!que.empty()){
int u=que.front();que.pop();
mark[u]=0; //!!!!
int len=edge[u].size();
for(int i=0;i<len;i++){
int v=edge[u][i].v;
int w=edge[u][i].w;
if(dist[v]>dist[u]+w){
dist[v]=dist[u]+w;
if(mark[v]==0){//spfa重点
mark[v]=1;
que.push(v);
}
}
}
}
}
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=0;i<=50000;i++){
edge[i].clear();
dist[i]=INF;//初始化
mark[i]=0;
}
int mi=INF,ma=-1;
for(int i=0;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
edge[b+1].push_back(node(a,-c));//大点到小点的边
mi=min(mi,a);
ma=max(ma,b+1);
}
for(int i=mi;i<ma;i++){//隐藏约束条件
edge[i].push_back(node(i+1,1));
edge[i+1].push_back(node(i,0));
}
spfa(ma,mi);//最大点到最小点
printf("%d\n",-dist[mi]);//结果负值
}
return 0;
}
//最远路求法,dist初始-INF
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3fffffff
int n;
struct node {
int v,w;
node(int v_=0,int w_=0):v(v_),w(w_){}
};
vector <node> edge[50001];
int dist[50001];
int mark[50001];
void spfa(int s,int e){
queue <int> que;
que.push(s);
mark[s]=1;
dist[s]=0;
while(!que.empty()){
int u=que.front();que.pop();
mark[u]=0;
int len=edge[u].size();
for(int i=0;i<len;i++){
int v=edge[u][i].v;
int w=edge[u][i].w;
if(dist[v]<dist[u]+w){//不要等于!!!!
dist[v]=dist[u]+w;
if(mark[v]==0){
mark[v]=1;
que.push(v);
}
}
}
}
}
int main(){
while(~scanf("%d",&n)){
for(int i=0;i<=50000;i++){
edge[i].clear();
mark[i]=0;
dist[i]=-INF;//初始化
}
int mi=50000+10,ma=-1;
for(int i=0;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
edge[a].push_back(node(b+1,c));
mi=min(mi,a);
ma=max(ma,b+1);
}
for(int i=mi;i<ma;i++){
edge[i].push_back(node(i+1,0));
edge[i+1].push_back(node(i,-1));
}
spfa(mi,ma);
printf("%d\n",dist[ma]);
}
return 0;
}