用一下百度搜索 差分约束 得到的解释
如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。
差分约束系统的求解要利用单源最短路径问题中的三角形不等式,即对有向图(或无向图)中的任何一条边<u,v>都有 d(v)<=d(u)+edge[u][v] 其中:d(u)和d(v)是求得的从源点分别到顶点u和顶点v的最短路径长度 edge[u][v]是边<u,v>的权值
如果有向网中存在负权值回路 则对应的差分约束系统就无解
我个人的感觉的是首先从题目中找出不等式 写成xi-xj<ak的形式 然后根据这个形式构图 <j,i>边的权值为ak
然后根据源点 求出相应的值(最小值)有的还需要判断是否有解!!
zoj 2770
给出每个兵营的容量 以及第i个兵营到第j个兵营拥有的最少的士兵数
求第1个到第n个兵营最少的士兵总和
设前i个兵营士兵总和为Si 期中S0=0
第i个到第j个兵营 士兵总数至少有k个 则Sj-Si>=k 则Si-Sj<=-k
第i个到第j个兵营 士兵总数和不会超过这些兵营士兵容量的和 由此也可以得到一些不等式
每个兵营的士兵数不会超过容量 Si-S(i-1)<=Ci
每个兵营的士兵数>=0 Si-S(i-1)>=0 S(i-1)-Si<=0
分别根据这些不等式建图
然后求路径最小值
<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#define eps 1e-8
#define op operator
#define MOD 10009
#define MAXN 1010
#define INF 9999999
#define MEM(a,x) memset(a,x,sizeof a)
#define ll __int64
using namespace std;
int n,m;//n个兵营 m条消息(m行数据)
int c[MAXN];//兵营的容量 最多能容纳的士兵数量
int dist[MAXN];//从源点到各顶点的最短距离 Sn为源点
int d[MAXN];//前i个兵营容量和
int ei;//边的序号
struct edge
{
int u,v,w;//边的起点 终点 权值
};
edge e[25010];
void init()
{
ei=0;
for(int i=0;i<n;i++)
dist[i]=INF;
dist[n]=0;
d[0]=0;
}
bool Bellman_ford()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<ei;j++)
{
int t=dist[e[j].u]+e[j].w;
if(dist[e[j].u]!=INF&&t<dist[e[j].v])
dist[e[j].v]=t;
}
}
//检查 若还有更新说明存在无限循环的负值回路
for(int i=0;i<ei;i++)
{
if(dist[e[i].u]!=INF&&dist[e[i].u]+e[i].w<dist[e[i].v])
return 0;
}
return 1;
}
int main()
{
//freopen("ceshi.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF)
{
int u,v,w;
init();
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
e[ei].u=i-1;
e[ei].v=i;
e[ei].w=c[i];
ei++;
e[ei].u=i;
e[ei].v=i-1;
e[ei].w=0;
ei++;
d[i]=d[i-1]+c[i];
}
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&w);
e[ei].u=v;
e[ei].v=u-1;
e[ei].w=-w;
ei++;
e[ei].u=u-1;
e[ei].v=v;
e[ei].w=d[v]-d[u-1];
ei++;
}
if(Bellman_ford())
printf("%d\n",dist[n]-dist[0]);
else
printf("Bad Estimations\n");
}
return 0;
}
</span>
zoj 1508 poj 1201 Intervals
有一个整数集合Z 给定n个整数闭区间[ai,bi] Z集合有第i个集合相交至少有ci个元素
求Z集合中元素最少的个数
设S[i]为Z集合中 小于等于i的元素个数 则 S[bi]-S[ai-1]>=ci
即S[ai-1]-S[bi]<=-ci
根据实际情况还有两个约束条件 S[i]-S[i-1]<=1 S[i-1]-S[i]<=0
如果把这些约束条件都建成边 便有3*50000条边 并不是一个好方法
S[i] <= S[i-1] + 1 等效于 S[i] – S[mx] <= S[i-1] – S[mx] + 1。
假设dist[i]为源点mx 到顶点Si 的最短路径,那么S[i] – S[mx]就是dist[i],S[i-1] – S[mx] + 1
就是dist[i-1] + 1,即如果顶点Si 到源点的最短路径长度大于Si-1 到源点的最短路径长度加1,则
修改dist[i]为dist[i-1] + 1。
S[i-1]<=S[i] 等效于 S[i-1] – S[mx] <= S[i] – S[mx]。
S[i] – S[mx]就是dist[i],S[i-1] – S[mx]就是dist[i-1],即如果顶点Si-1 到源点的最短路径长度
大于Si 到源点的最短路径,则修改dist[i-1]为dist[i]。
所以只有一步是用来建边的 且边的权值为负 根据约束条件 及时修改S[i]的值
<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#define eps 1e-8
#define op operator
#define MOD 10009
#define MAXN 50100
#define INF 100000
#define MEM(a,x) memset(a,x,sizeof a)
#define ll __int64
using namespace std;
int mn,mx;//区间最小值 最大值
int dist[MAXN];
int n;//区间个数
struct edge
{
int u,v,w;
};
edge e[MAXN];
void init()
{
MEM(dist,0);
mx=0; mn=INF;
}
void Bellman_ford()
{
int flag=1;
while(flag)
{
flag=0;
for(int i=0;i<n;i++)
{
int t=dist[e[i].u]+e[i].w;
if(dist[e[i].v]>t)
{
dist[e[i].v]=t;
flag=1;
}
}
//根据s[i]<=s[i-1]+1 修改s[i]的值
for(int i=mn;i<=mx;i++)
{
int t=dist[i-1]+1;
if(dist[i]>t)
{
dist[i]=t;
flag=1;
}
}
for(int i=mx;i>=mn;i--)
{
int t=dist[i];
if(dist[i-1]>t)
{
dist[i-1]=t;
flag=1;
}
}
}
}
int main()
{
//freopen("ceshi.txt","r",stdin);
while(scanf("%d",&n)!=EOF)
{
//<v,u-1>权值-w
init();
int u,v,w;
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
e[i].u=v;
e[i].v=u-1;
e[i].w=-w;
if(v>mx) mx=v;
if(u<mn) mn=u;
}
Bellman_ford();
printf("%d\n",dist[mx]-dist[mn-1]);
}
return 0;
}
</span>
poj 3169 Layout
给母牛安排位置 有两种限制
(1)ML个约束 两个母牛之间能够分隔开的最大距离
(2)MD个约束 两个母牛之间必须分隔开的最小距离
求第1头母牛与第N头母牛之间距离的最大值
针对 ML约束 Si Sj Dk Sj-Si<=Dk <i,j>权值为Dk
针对 MD约束 Si Sj Dk Sj-Si>=Dk 即Si-Sj<=-Dk <j,i>权值为-Dk
按着这些约束建边就可以 求路径最小值 如果不存在满足条件的排列就输出-1 即存在负环
第i头与第n头母牛直接的距离可以任意 输出-2 dist[n]=INF
否则直接输出最大距离
以第一个位置作为源点建图 约束条件相对简单 建图就比较容易了
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#define eps 1e-8
#define op operator
#define MOD 10009
#define MAXN 20010
#define INF 0x7fffffff
#define MEM(a,x) memset(a,x,sizeof a)
#define ll __int64
using namespace std;
int n,ml,md;
ll dist[1010];
int cnt;
struct edge
{
int u,v;
ll w;
};
edge e[MAXN];
void init()
{
for(int i=0;i<=n;i++)
dist[i]=INF;
dist[1]=0;
cnt=0;
}
bool Bellman_ford()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<cnt;j++)
{
ll t=dist[e[j].u]+e[j].w;
if(dist[e[j].u]!=INF&&t<dist[e[j].v])
dist[e[j].v]=t;
}
}
for(int i=0;i<cnt;i++)
if(dist[e[i].u]!=INF&&dist[e[i].u]+e[i].w<dist[e[i].v])
return 0;
return 1;
}
int main()
{
//freopen("ceshi.txt","r",stdin);
while(scanf("%d%d%d",&n,&ml,&md)!=EOF)
{
init();
for(int i=0;i<ml;i++)
{
int u,v;
ll d;
scanf("%d%d%I64d",&u,&v,&d);
e[cnt].u=u; e[cnt].v=v; e[cnt].w=d;
cnt++;
}
for(int i=0;i<md;i++)
{
int u,v;
ll d;
scanf("%d%d%I64d",&u,&v,&d);
e[cnt].u=v; e[cnt].v=u; e[cnt].w=-d;
cnt++;
}
int flag=Bellman_ford();
// for(int i=0;i<=n;i++)
// cout<<dist[i]<<" ";
// cout<<endl;
if(!flag)
printf("-1\n");
else
{
if(dist[n]==INF)
printf("-2\n");
else printf("%I64d\n",dist[n]-dist[1]);
}
}
return 0;
}
zoj 1455 Schedule Problem
完成一个项目 把一个项目分成若干部分 每个部门都需要一定的时间去完成
各个部分之间有一定的约束条件
FAS: a,b
S[a]+time[a]>=S[b]
FAF: a,b
S[a]+time[a]>=S[b]+time[b]
SAF: a,b
S[a]>=S[b]+time[b]
SAS: a,b
S[a]>=S[b]
以0作为源点建图 但是在建图方式上有点问题 我一开始建图的方法跑不出正确答案
第一种方法:
就是我之前想的方式 建图 <a,b>为边建边 但是0与个点建边的权值为-ti[i]
且最后要找出 dist中最小值 最后输出的值 为dist[i]与这个最小值的差
第二种方法:
在根据约束条件建边之后 还要加上 每个点与源点之间的边 边的权值为0
但是在写Bellman_ford()时 要将大小判断改变 dist[e[i].v]要取大值
第一种方法代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#define eps 1e-8
#define op operator
#define MOD 10009
#define MAXN 100100
#define INF 1000000
#define MEM(a,x) memset(a,x,sizeof a)
#define ll __int64
using namespace std;
int n,cnt;
int ti[10010];
int dist[10010];
int s;
struct edge
{
int u,v,w;
};
edge e[MAXN];
void init()
{
for(int i=1;i<=n;i++)
dist[i]=INF;
dist[0]=0;
cnt=0;
}
void addedge(int a,int b,int w)
{
e[cnt].u=a;
e[cnt].v=b;
e[cnt].w=w;
cnt++;
}
bool Bellman_ford()
{
for(int i=1;i<=n;i++)
{
for(int j=0;j<cnt;j++)
{
int t=dist[e[j].u]+e[j].w;
if(t<dist[e[j].v])
dist[e[j].v]=t;
}
}
for(int i=0;i<cnt;i++)
if(dist[e[i].u]+e[i].w<dist[e[i].v])
return 0;
int mi=INF;
for(int i=1;i<=n;i++)
{
if(mi>dist[i])
{
mi=dist[i];
s=i;
}
}
return 1;
}
int main()
{
//freopen("ceshi.txt","r",stdin);
int cs=1;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
init();
for(int i=1;i<=n;i++)
scanf("%d",&ti[i]);
getchar();
char ch[10];
while(1)
{
scanf("%s",ch);
if(ch[0]=='#')
break;
int a,b;
scanf("%d%d",&a,&b);
if(strcmp(ch,"FAS")==0)
{
addedge(a,b,ti[a]);
// addedge(b,a,-ti[a]);
}
if(strcmp(ch,"FAF")==0)
{
addedge(a,b,ti[a]-ti[b]);
// addedge(b,a,ti[b]-ti[a]);
}
if(strcmp(ch,"SAF")==0)
{
addedge(a,b,-ti[b]);
// addedge(b,a,ti[b]);
}
if(strcmp(ch,"SAS")==0)
{
addedge(a,b,0);
// addedge(b,a,0);
}
}
for(int i=1;i<=n;i++)
addedge(0,i,-ti[i]);
int flag=Bellman_ford();
printf("Case %d:\n",cs++);
if(!flag)
puts("impossible");
else
{
for(int i=1;i<=n;i++)
printf("%d %d\n",i,dist[i]-dist[s]);
}
puts("");
}
return 0;
}
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#define eps 1e-8
#define op operator
#define MOD 10009
#define MAXN 100100
#define INF 1000000
#define MEM(a,x) memset(a,x,sizeof a)
#define ll __int64
using namespace std;
int n,cnt;
int ti[10010];
int dist[10010];
int s;
struct edge
{
int u,v,w;
};
edge e[MAXN];
void init()
{
for(int i=1;i<=n;i++)
dist[i]=-INF;
dist[0]=0;
cnt=0;
}
void addedge(int a,int b,int w)
{
e[cnt].u=a;
e[cnt].v=b;
e[cnt].w=w;
cnt++;
}
bool Bellman_ford()
{
for(int i=1;i<=n;i++)
{
for(int j=0;j<cnt;j++)
{
int t=dist[e[j].u]+e[j].w;
if(t>dist[e[j].v])
dist[e[j].v]=t;
}
}
for(int i=0;i<cnt;i++)
if(dist[e[i].u]+e[i].w>dist[e[i].v])
return 0;
return 1;
}
int main()
{
//freopen("ceshi.txt","r",stdin);
int cs=1;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
init();
for(int i=1;i<=n;i++)
scanf("%d",&ti[i]);
getchar();
char ch[10];
while(1)
{
scanf("%s",ch);
if(ch[0]=='#')
break;
int a,b;
scanf("%d%d",&a,&b);
if(strcmp(ch,"FAS")==0)
{
// addedge(a,b,time[a]);
addedge(b,a,-ti[a]);
}
if(strcmp(ch,"FAF")==0)
{
// addedge(a,b,time[a]-time[b]);
addedge(b,a,ti[b]-ti[a]);
}
if(strcmp(ch,"SAF")==0)
{
// addedge(a,b,-time[b]);
addedge(b,a,ti[b]);
}
if(strcmp(ch,"SAS")==0)
{
addedge(b,a,0);
}
}
for(int i=1;i<=n;i++)
addedge(0,i,0);
int flag=Bellman_ford();
printf("Case %d:\n",cs++);
if(!flag)
puts("impossible");
else
{
for(int i=1;i<=n;i++)
printf("%d %d\n",i,dist[i]);
}
puts("");
}
return 0;
}
//差分约束系统 要从题目中找出相应的约束条件 构造合理的序列 有时需要根据现实情况 添加相应的约束条件
比如 在整数的情况下 序列和 前后项之间的一些不等关系