POJ 3169 Layout(差分约束)

Layout

题目链接:

http://poj.org/problem?id=3169

解题思路:

题目大意:

当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些。FJ有N(2<=N<=1000)头奶牛,编号从1到N,沿一条直线站着等候喂

食。奶牛排在队伍中的顺序和它们的编号是相同的。因为奶牛相当苗条,所以可能有两头或者更多奶牛站在同一位置上。即使说,

如果我们想象奶牛是站在一条数轴上的话,允许有两头或更多奶牛拥有相同的横坐标。

一些奶牛相互间存有好感,它们希望两者之间的距离不超过一个给定的数L。另一方面,一些奶牛相互间非常反感,它们希望两者间

的距离不小于一个给定的数D。给出ML条关于两头奶牛间有好感的描述,再给出MD条关于两头奶牛间存有反感的描述。

(1<=ML,MD<=10000,1<=L,D<=1000000)

你的工作是:如果不存在满足要求的方案,输出-1;如果1号奶牛和N号奶牛间的距离可以任意大,输出-2;否则,计算出在满足所

有要求的情况下,1号奶牛和N号奶牛间可能的最大距离。

算法思想:

我们先研究,如果不要求输出1和N的最大距离,而只需一个可行的距离,应该如何操作。

我们用D[i]表示I号奶牛和1号奶牛间的距离。

因为在队伍中的顺序必须和编号相同,所以对于任意I号奶牛,1<=I<N,在距离上应该满足:

D[I+1] - D[I] >= 0

对于每个好感的描述(i,j,k),假设i<=j,体现到距离上的要求就是:

D[j] - D[I] <= k

对于每个反感的描述(i,j,k),假设i<=j,体现到距离上的要求就是:

D[j] - D[I] >= k

这时的模型有一个名称,叫作:差分约束系统。

为了方便起见,我们将每种不等式写成我们约定的形式:

D[I] <= D[I+1]
D[j] <= D[I] + k
D[I] <= D[j] - k

看到这些不等式,你想到了什么?

没错,在求顶点间地最短路问题中,我们有这样的不等式:

若顶点u到顶点v有边e=uv,且边权为w(e),设d(u),d(v)为源点到顶点u和顶点v的最短路长,

则 d(v) <= d(u) + w(e)

这个不等式和前面的条件形式十分相似,这就启发我们用构图用最短路做。

具体步骤是:

作有向图G=(V,E),V={ v1,v2,v3,…,vn},E={e1,e2,e3,…},对于相邻两点i和(i+1),对应的顶点vi+1向vi引一条边,费用

为0;对于每组好感描述(ai,bi,di),我们假设有ai<bi,否则ai和bi交换,则顶点vai向vbi引一条边,费用为di;对于每组反感描述

(ai,bi,di),我们假设有ai<bi,否则ai和bi交换,则顶点vbi向vai引一条边,费用为-di。

于是问题变为在G中求v1到其它所有顶点的最短路。我们证明若G中无负权回路,则问题有解,即存在满足条件的数列,若G中有负

权回路,则问题无解,即不存在满足条件的数列。

AC代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define INF 0xfffffff
using namespace std;

const int V=1010;
const int E=20010;
int n,ml,md;
int pnt[E],cost[E],nxt[E];
int e,head[V],dis[V];
int vis[V];
int cnt[V];

int relax(int u,int v,int c){
    if(dis[v] > dis[u]+c){
        dis[v] = dis[u]+c;
        return 1;
    }
    return 0;
}

inline void addedge(int u,int v,int c){
    pnt[e] = v;
    cost[e] = c;
    nxt[e] = head[u];
    head[u] = e++;
}

int SPFA(int src,int n){
    memset(cnt,0,sizeof(cnt));
    memset(vis,0,sizeof(vis));
    for(int i = 1; i <= n; ++i)
        dis[i] = INF;
    dis[src] = 0;
    queue<int> q;
    q.push(src);
    vis[src] = 1;
    ++cnt[src];
    while(!q.empty()){
        int u,v;
        u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; i != -1; i = nxt[i]){
            v = pnt[i];
            if(relax(u,v,cost[i]) && !vis[v]){
                q.push(v);
                vis[v] = 1;
                if(++cnt[v] > n)
                    return -1;
            }
        }
    }
    if(dis[n] == INF)
        return -2;
    return dis[n];
}

int main(){
    while(~scanf("%d%d%d",&n,&ml,&md)){
        int a,b,c;
        e = 0;
        memset(head,-1,sizeof(head));
        for(int i = 0; i < ml; ++i){
            scanf("%d%d%d",&a,&b,&c);
            if(a > b)
                swap(a,b);
            addedge(a,b,c);
        }
        for(int i = 0; i < md; ++i){
            scanf("%d%d%d",&a,&b,&c);
            if(a < b)
                swap(a,b);
            addedge(a,b,-c);
        }
        printf("%d\n",SPFA(1,n));
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值