POJ_3169 Layout(差分约束系统-最短路)

题意:

一群牛按照编号排列,但是个别之间有限制。有的不能挨得太近,有的不能离得太远。问第1头牛和第N头之间最远是多少?

思路:

最开始想着是贪心,但是发现不好贪,牛是按照序号排列的,但是关系却很复杂。之后参考了网上的思路知道是差分约束系统,但是自己理解起来还是换了一些功夫。

首先将题目中的关系均转化为不等式:

隐藏条件:

dis[i+1]>=dis[i]

限制条件:

dis[b]-dis[a]<=c

dis[b]-dis[a]>=c

目标结果:

dis[N]-dis[1]<=res

这些不等式的特点是所有式子的两边都只出现了一个变量。(差分约束系统)

我们这样理解:记从起点s出发,到各个顶点v的最短距离为dis[v]。因此,对于每条权值为w的边e=(v,u),都有dis[v]+w>=dis[u](因为最短路径不一定经过这条边)。所以在满足这些约束不等式的dis中,dis[v]-dis[s]的最大值就是从s到v的最短距离。

将这些不等式转换:

dis[i]-dis[i+1]<=0

dis[b]-dis[a]<=c

dis[a]-dis[b]<=-c

dis[N]-dis[1]<=res

则原问题转换成了一个图论中的最短路问题,0/c/-c转化为点与点之间的边的权值,e(i+1,i,0),e(a,b,c),e(b,a,-c)。因为边的权包含负值,我们选择Bellman或者SPFA来解决。

另外输出结果对应含负权最短路三种状态:

res=-1:图中包含负权回路,无解。(原题中的无解情况为对应点的相对位置和序号冲突,等价为负权回路的情况)(另:负权回路:一个环上的所有权值之和为一个负数

res=-2:最短路的结果dis[N]=INF,N和1之间的距离可以无限大。

res=?:N与1之间的最短路,也就是N到1之间的最大距离。

代码实现:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

const int MAX_N = 1005;
const int INF = 0x7ffffff;

struct Edge{
    int to;
    int cost;
    Edge( int to, int cost){
        this->to = to;
        this->cost = cost;
    };
};

int N,ML,MD;
int cnt[MAX_N];
int dist[MAX_N];
bool mark[MAX_N];
vector<Edge> edge[MAX_N];
int spfa(int v);
int main()
{
    while( scanf("%d%d%d",&N,&ML,&MD) != EOF ){
        for( int i = 1; i <= N; i++ ){
            cnt[i] = 0;
            dist[i] = INF;
            mark[i] = false;
            edge[i].clear();
        }
        int a,b,c;
        //注意这里只能是单向,因为牛是按照编号顺序排列的
        for( int i = 0; i < ML; i++ ){
            scanf("%d%d%d",&a,&b,&c);
            edge[a].push_back(Edge(b,c));
        }
        for( int i = 0; i < MD; i++ ){
            scanf("%d%d%d",&a,&b,&c);
            edge[b].push_back(Edge(a,-c));
        }
        for( int i = 1; i < N; i++ ){
            edge[i+1].push_back(Edge(i,0));
        }
        int res = spfa(1);
        printf("%d\n",res);
    }
    return 0;
}

int spfa(int v){
    queue<int> que;
    que.push(v);
    cnt[v]++;
    dist[v] = 0;
    mark[v] = true;
    while( !que.empty() ){
        v = que.front();
        que.pop();
        mark[v] = false;
        int len = edge[v].size();
        for( int i = 0; i < len; i++ ){
            Edge tmp = edge[v][i];
            if( dist[tmp.to] > dist[v]+tmp.cost ){
                dist[tmp.to] = dist[v]+tmp.cost;
                if( mark[tmp.to] == false ){
                    que.push(tmp.to);
                    cnt[tmp.to]++;
                    mark[tmp.to] = true;
                    //SPFA判断副环,某个节点进入队列大于N次
                    if( cnt[tmp.to] > N ){
                        return -1;
                    }
                }
            }
        }
    }
    if( dist[N] == INF ){
        return -2;
    }
    else{
        return dist[N];
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值