P4878 [USACO05DEC]Layout G
题目链接:P4878 [USACO05DEC]Layout G
题意:按编号排了 n n n 只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离,问 1 1 1 到 n n n 的距离最大值
我们可以发现题目给的数据本质上就是 v − u ≥ d i v-u\ge d_i v−u≥di 或 v − u ≤ d i v-u \le d_i v−u≤di
这是什么?差分约束!
差分约束:根据三角不等式 d u + w ( u , v ) ≥ d v d_u + w(u,v) \ge d_v du+w(u,v)≥dv 的构成,我们可以把形如 v − u ≥ d i v-u\ge d_i v−u≥di 的不等式转化为
v − d i ≥ u v-d_i\ge u v−di≥u (可以看作 v v v 向 u u u 连了一条边权为 − d i -d_i −di 的有向边),同理 v − u ≤ d i v-u \le d_i v−u≤di 转化为 u + d i ≥ v u+d_i \ge v u+di≥v
这里不过多讲解差分约束了
那么为什么差分约束后求最短路就是最大值呢?
因为求最短路是由无穷大向下不断约束得到的,因此得到的是最大值(同理求最长路就是最小值)
那我们只要按题意建图就行了
要注意的是,每个奶牛 i i i 满足 d i − 1 ≤ d i d_{i-1} \le d_{i} di−1≤di ,其中 d d d 表示所在位置,因此相邻的也要建边
为什么要强调这一点呢?
如下图:
如果没有建边,会发现答案为 5 5 5 (如上图所示)
如果建了边,答案才正确(该情况无解)
那现在只要从 1 1 1 开始跑SPFA就好了
但是还有问题, 1 1 1 并不能保证与所有结点连通,而我们知道差分约束无解的情况就是图中有负环
这个问题好解决,我们先在原图上建一个超级结点 0 0 0 与所有结点相连(边权为 0 0 0 ),在 0 0 0 跑一次SPFA判断即可判断解的情况
说了这么多,是不是有点晕(
理一下思路:
- 差分约束建图
- 相邻编号建图
- 建 0 0 0 结点判断解的情况
- 有解则从 1 1 1 开始跑SPFA
代码如下
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define R register
#define MAXN (int)(1e3+5)
#define MAXM (int)(2e4+5)
#define INF 0x3f3f3f3f3f3f3f3f
template<typename T>inline void read(R T &k)
{
R char ch=getchar();R T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
k=x*f;
}
int n,ml,md,m,d[MAXN],vis[MAXN],cnt[MAXN];
struct Edge
{
int u,v,w,next;
}e[MAXM<<1];
int head[MAXN],pos=1;
void add(R int u,R int v,R int w)
{
e[pos]={u,v,w,head[u]};
head[u]=pos++;
}
bool spfa(R int st)
{
queue<int> q;q.push(st);
memset(d,0x3f,sizeof(d));
vis[st]=1;d[st]=0;cnt[st]=1;
while(!q.empty())
{
R int u=q.front();
q.pop();vis[u]=0;
for(R int i=head[u]; i; i=e[i].next)
{
R int v=e[i].v,w=e[i].w;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
if(!vis[v])
{
if(++cnt[v]>n)
return 0; // 有0结点,结点数增加1,判负环略有区别
vis[v]=1;
q.push(v);
}
}
}
}
return 1;
}
signed main()
{
read(n);read(ml);read(md);
for(R int i=1,u,v,w; i<=ml; i++)
{
read(u);read(v);read(w); // d_v-d_u<=w -> d_v<=d_u+w
add(u,v,w);
}
for(R int i=1,u,v,w; i<=md; i++)
{
read(u);read(v);read(w); // d_v-d_u>=w -> d_v-w>=d_u
add(v,u,-w);
}
for(R int i=1; i<n; i++)
add(i+1,i,0); // d_i<=d_{i+1}+0
for(R int i=1; i<=n; i++)
add(0,i,0);
if(!spfa(0))return puts("-1"),0;
spfa(1);
if(d[n]==INF)puts("-2");
else printf("%lld\n",d[n]);
return 0;
}
转载请说明出处