题目大意:一条直线上有n个点,现给出ml个条件(i,j,w),描述第i个点到第j个点之间的距离小于等于w,和md个条件(i,j,w),描述第i个点到第j个点之间的距离小于等于w。现在请问第1个点到第n个点之间的距离最大是多少?如果是无限大,输出-2;如果无解,输出-1。
数据范围:n<=1000 ml,md<=10000,w<=1000000。
分析:首先形式化问题,设f(x)为第x个点所对应的位置坐标。那么题目转化为:
已知若干个形如f(j)-f(i)<=w的不等式,求f(n)-f(1)的最大值。
求不等式组的解集很明显是一个差分约束问题。
f(j)-f(i)<=w,变形以后变成f(j)<=f(i)+w。因为这个式子类似求最短路中的松弛操作,所以用图论的想法来看待这个式子,那么就可以发现:i到j有一条权值为w的边,并且i不能松弛j。逗号前面的解释了怎样建图,逗号后面的说明了在求f(n)-f(1)的最大值的时候,在对应的图里面要求从1到n的最短路,这个可以用spfa跑。
如果图中有负权环,那么就无解(spfa可以判断),如果1到n不连通,就是无限大(可以自己尝试证明一下)。
下面是代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 999999999
using namespace std;
struct node{
int v,next,w;
}edge[30000];
int head[30000],n,ml,md,cnt,dis[1010],num[1010];
bool vis[1010];
queue<int>q;
void addedge(int u,int v,int w)
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
bool spfa()
{
for(int i=2;i<=n;++i)
dis[i]=inf;
vis[1]=1;
q.push(1);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v,w=edge[i].w;
if(dis[u]+w<dis[v])
{
dis[v]=dis[u]+w;
if(!vis[v])
{
if(num[v]>n)return 1;//判断负权环路
num[v]++;
q.push(v);
vis[v]=1;
}
}
}
}
return 0;
}
int main()
{
memset(head,-1,sizeof head);
scanf("%d%d%d",&n,&ml,&md);
for(int i=0;i<ml;++i)//f(u)-f(v)<=w -> f(u)<=f(v)+w
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
for(int i=0;i<md;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);//f(u)-f(v)>=w -> f(v)<=f(u)-w
addedge(v,u,-w);
}
for(int i=1;i<n;++i)//题目有个隐藏条件:f(i+1)>f(i)
addedge(i+1,i,0);
if(spfa())printf("-1\n");
else if(dis[n]==inf)printf("-2\n");//不连通
else printf("%d\n",dis[n]);
}