PKU POJ 3169 Layout

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

問題概要

N匹の牛を飼っている。
牛は1〜N番目の牛まで順番に並んでいる。
いくつかの組み合わせについて、その牛同士の距離について、上限あるいは下限が与えられる。

実現できる最後の牛と最初の牛の最大の距離を求めよ。
ただし、そのような並び方がない場合は-1, 最大距離がどこまでも伸ばせる場合は、-2を答えよ。
牛同士は同じ場所にも立てる。

解法

ベルマンフォード。

なぜ最短経路問題なのか。

最短経路問題とは、全てのエッジについて、
d[i] + c >= d[j] (iからjに伸びるコストcのエッジ, d[i]はi番目のノードの始点からの最短距離)
という不等式を満たす、d[s] = 0 (sは始点) とした時の、
全てのノードiについてのd[i]の最大値を求める問題である。

今回の問題を最短経路問題のような不等式にしてみる。

d[i]を最初の牛からの距離とする。

  1. 牛i, i+1について、牛は順番に並んでることから、
    • d[i] <= d[i+1]
  2. 牛i, jについて上限が決まってる場合、上限の距離をcとすると、
    • d[i] + c >= d[j] ( j > i )
  3. 牛i, jについて下限が決まってる場合、下限の距離をcとすると、
    • d[i] + c <= d[j] (j > i)

となる。
最短経路問題に当てはめるために式を変形すると、

  1. d[i+1] + 0 >= d[i]
  2. d[i] + c >= d[j]
  3. d[j] - c >= d[i]

と変形できる。
そして、始点は最初の牛にするので、

  • d[0] = 0

よって、これらの式を全て満たす最大のd[N-1]をベルマンフォードで求めることができる。
最大距離が無限の場合は、d[N-1]がINFのまま、
解がない場合は、ベルマンフォードの性質から負のサイクルを検出できる。
負のサイクルを持つということは、どこまでもd[i]の値が小さくなる、詰まるころ満たす値が存在しない
つまるところ、解がない、ということになる。

ソース

#include<iostream>
#include<algorithm>
#include<vector>

#define REP(i,b,n) for(int i=b;i<(int)n;i++)
#define rep(i,n)   REP(i,0,n)

using namespace std;
const int INF = 1000000000;
const int N = 1009;

struct Edge{
  int from, to, cost;
};

int d[N], n;

int bellman_ford(vector<Edge> &E, int s){
  rep(i, N)d[i] = INF;
  d[s] = 0;
  rep(i, n){
    rep(j, E.size()){
      Edge e = E[j];
      if(d[e.from] != INF && d[e.to] > d[e.from] + e.cost){
        d[e.to] = d[e.from] + e.cost;
        if(i == n-1)return -1;
      }
    }
  }
  if(d[n-1] >= INF){
    return -2;
  }
  return d[n-1];
}

int main(){
  int L, D;
  cin >> n >> L >> D;
  vector<Edge> E;
  rep(i, n-1){
    E.push_back((Edge){i+1, i+0, 0});
  }
  rep(i, L){
    int from, to, c;
    cin >> from >> to >> c;
    if(to < from)swap(from, to);
    E.push_back((Edge){from-1, to-1, c});
  }
  rep(i, D){
    int from, to, c;
    cin >> from >> to >> c;
    if(to < from)swap(from, to);
    E.push_back((Edge){to-1, from-1, -c});
  }
  cout << bellman_ford(E, 0) << endl;;
  return 0;
}