差分约束系统使用心得及dy引擎分析

差分约束系统是一个神奇的数与图结合的算法。
如果有一堆又一堆形如:
x1-x2<=k1
x2-x1<=k2
x4-x1<=k3
x4-x2<=k4
……
这样的恶心不等式,我们怎么确定x1-x4的值呢?

这是不可能的,因为我们没有一个确定的量,我们只知道一些数的差。

但是我们可以求出每个数之间的固定的差是多少,前提是给我们的条件只有一组差的解。

这时候差分约束系统便用了用途。

设有x-y<=k,那么我们就给y连一条到x的权值为k的有向边,之后用SPFA跑一个最短路。

如果题目给了我们一些确定的量,我们就把它们放入图中。如果没有给,那么就自己设一个。

很明显可能有负权环,这样的话就有无数组解了。

如果有的点遍历不到,也会有无数组解。

x-y<=k(==)y-x>=-k

我们也可以给x到y连一条权值为-k的有向边,之后跑一个最大路。

如果有正权环也是有无数组解的。

这就是差分约束系统的实现了。

当然题目可不会这么明确地把所有条件给我们,可能有许多隐藏的条件需要自己去发现。

我们来结合实际谈一下。

JZOJ3430

DY引擎

Description

BOSS送给小唐一辆车。小唐开着这辆车从PKU出发去ZJU上课了。

众所周知,天朝公路的收费站超多的。经过观察地图,小唐发现从PKU出发到ZJU的所有路径只会有N(2<=N<=300)个不同的中转点,其中有M(max(0, N-100) <=M<=N)个点是天朝的收费站。N个中转点标号为1…N,其中1代表PKU,N代表ZJU。中转点之间总共有E(E<=50,000)条双向边连接。

每个点还有一个附加属性,用0/1标记,0代表普通中转点,1代表收费站。当然,天朝的地图上面是不会直接告诉你第i个点是普通中转点还是收费站的。地图上有P(1<=P<=3,000)个提示,用[u, v, t]表示:[u, v]区间的所有中转点中,至少有t个收费站。数据保证由所有标记得到的每个点的属性是唯一的。

车既然是BOSS送的,自然非比寻常了。车子使用了世界上最先进的DaxiaYayamao引擎,简称DY引擎。DY引擎可以让车子从U瞬间转移到V,只要U和V的距离不超过L(1<=L<=1,000,000),并且U和V之间不能有收费站(小唐良民一枚,所以要是经过收费站就会停下来交完钱再走)。

DY引擎果然是好东西,但是可惜引擎最多只能用K(0<=K<=30)次。

Input

第一行有6个整数N,M,E,P,L,K分别代表:N个中转点,M个收费站,E条边,P个提示,DY引擎的有效距离L,DY引擎的使用次数K。

接下去E行,每行有3个整数u,v,w(1<=u, v<=N; 1<=w<=1,000,000)表示:u和v之间有一条长度为w的双向边。

接下去P行,每行有3个整数u,v,t(1<=u<=v<=N; 0<=t<=u-v+1)表示: [u, v] 标号区间至少有t个收费站。

Output

输出一个整数,表示小唐从PZU开到ZJU用的最短距离(瞬间转移距离当然是按0来计算的)。

Sample Input

6 2 6 2 5 1

1 2 1

2 3 2

3 6 3

1 4 1

4 5 2

5 6 3

2 5 2

4 6 2

Sample Output

1

【样例解释】

4、5是收费站。1->2(1)->6(1)

对于30%的数据保证:

2<=N<=30,max(0, N-10) <=M<=N,0<=k<=10

对于100%的数据保证:

2<=N<=300,max(0, N-100) <=M<=N,E<=50,000,1<=P<=3,000,1<=L<=1,000,000,0<=K<=30

第一部分求出加油站是哪些中转点:
设p[i]表示中转点1-i的加油站的个数。根据题目给的条件,我们可以列出不等式:

p[u-1]-p[v]<=-t

还有隐藏的条件:

p[i]-p[i-1]<=1,p[i-1]-p[i]<=0

我们给所有的v到u-1连一条权值是-t的边,给i-1到i连一条权值是1的边,给i到i-1连一条权值是0的边。

我们又知道p[n]=m,把n作为起始点,然后SPFA一遍,p[i]-p[i-1]就是i的状态。

第二部分也是SPFA,就是维护的状态多了一点。

Floyed预处理出f[i][j]表示i到j的最短距离,注意当我们枚举k为中转点时,k不能是加油站。因为从i到j间是不能有加油站的。

设d[i][j]表示从1走到i,飞了j次的最短距离。d[1][0]=0。

每次枚举一个点q,一看能否更新d[q][j],用i到q的边维护,相当于普通的SPFA。二看能否更新d[q][j+1],条件是(f[i][q]<=l&&j<=k&&d[i][j]

#include<cstdio>
#include<cstring>
#define fo(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
const int maxx=1684300900;
int min(int x,int y){return x<y?x:y;} int max(int x,int y){return x>y?x:y;}
int i,j,n,m,e,p,l,k,u,v,w,ans=maxx,d1[1000000],d2[301],d[1000000][2],bz[301][301],ff[301][301],fp[301][301],bx[301],f[301][301],t[301][301],dp[301][31],s[301][31];
void init();
void A();
void B();
void Floyed();
int main()
{
 init();
 A();
 Floyed();
 B();
 fo(i,0,k) ans=min(ans,dp[n][i]);
 printf("%d\n",ans);
}
void init()
{
 memset(f,100,sizeof(f));
 memset(ff,100,sizeof(ff));
 memset(d2,100,sizeof(d2));d2[0]=0;
 memset(dp,100,sizeof(dp));dp[1][0]=0;
 scanf("%d%d%d%d%d%d",&n,&m,&e,&p,&l,&k);
 fo(i,1,e)
 {
 scanf("%d%d%d",&u,&v,&w);
 f[u][v]=f[v][u]=min(f[v][u],w);
 if(!bz[u][v])
 {
 bz[u][v]=1;
 bz[v][u]=1;
 }
 }
 fo(i,1,n)fo(j,1,n)t[i][j]=f[i][j];
 fo(i,1,p)
 {
 scanf("%d%d%d",&u,&v,&w);
 if(-w<ff[v][u-1]) ff[v][u-1]=-w;
 }
 fo(i,1,n)
 {
 if(0<ff[i][i-1]) ff[i][i-1]=0;
 if(1<ff[i-1][i]) ff[i-1][i]=1;
 }
 fo(i,0,n)fo(j,0,n)
 if(i!=j&&ff[i][j]<maxx) fp[i][++fp[i][0]]=j;

}
void A()
{
 d1[1]=n;d2[n]=m;bx[n]=1;
 i=0;j=1; 
 while(i<j)
 {
 i++; 
 int x=d1[i];
 fo(q,1,fp[x][0])
 {
 int y=fp[x][q];
 if(d2[x]+ff[x][y]<d2[y])
 {
 d2[y]=d2[x]+ff[x][y];
 if(!bx[y])
 {
 d1[++j]=y;
 bx[y]=1; 
 }
 } 
 }
 bx[d1[i]]=0;
 }
}
void Floyed()
{
 fo(i,1,n)
 fo(j,1,n)
 if(i!=j)
 fo(k,1,n)
 if((!(d2[k]-d2[k-1]))&&(i!=k)&&(j!=k)&&(f[i][k]<maxx)&&(f[k][j]<maxx)&&(f[i][k]+f[k][j]<f[i][j])) f[i][j]=f[i][k]+f[k][j];
}
void B()
{
 i=0;j=1; 
 d[1][0]=1;d[1][1]=0;s[1][0]=1;
 while(i<j)
 {
 i++;
 int x=d[i][0],y=d[i][1];
 fo(q,2,n)
 {
 if(dp[x][y]+t[x][q]<dp[q][y])
 {
 dp[q][y]=dp[x][y]+t[x][q];
 if(!s[q][y])
 {
 d[++j][0]=q;
 d[j][1]=y;
 s[q][y]=1;
 }
 }
 if(y<k&&f[x][q]<=l&&dp[x][y]<dp[q][y+1])
 {
 dp[q][y+1]=dp[x][y];
 if(!s[q][y+1])
 {
 d[++j][0]=q;
 d[j][1]=y+1;
 s[q][y+1]=1;
 }

 }
 }
 s[x][y]=0;
 }
}

你可能感兴趣的:(SPFA,信息学,差分约束系统)