差分约束系统是一个神奇的数与图结合的算法。
如果有一堆又一堆形如:
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
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)次。
第一行有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个收费站。
输出一个整数,表示小唐从PZU开到ZJU用的最短距离(瞬间转移距离当然是按0来计算的)。
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
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;
}
}