原题参考:http://www.usaco.org/index.php?page=viewproblem2&cpid=816&lang=zh
问题说明
FarmerJohn最讨厌的农活是运输牛粪。为了精简这个过程,他产生了一个新奇的想法:与其使用拖拉机拖着装满牛粪的大车从一个地点到另一个地点,为什么不用一个巨大的便便弹弓把牛粪直接发射过去呢?(事实上,好像哪里不太对……)FarmerJohn的农场沿着一条长直道路而建,所以他农场上的每个地点都可以简单地用该地点在道路上的位置来表示(相当于数轴上的一个点)。FJ建造了N个弹弓(1≤N≤10^5),其中第i个弹弓可以用三个整数xi,yi以及ti描述,表示这个弹弓可以在ti单位时间内将牛粪从位置xi发射到位置yi。FJ有M堆牛粪需要运输(1≤M≤10^5)。第j堆牛粪需要从位置aj移动到位置bj。使用拖拉机运输牛粪,经过路程d需要消耗d单位时间。FJ希望通过对每一堆牛粪使用至多一次弹弓来减少运输时间。FJ驾驶没有装载牛粪的拖拉机的时间不计。对这M堆牛粪的每一堆,在FJ可以在运输过程中使用至多一次弹弓的条件下,帮助FJ求出其最小运输时间。输入格式(文件名:slingshot.in):
输入的第一行包含N和M。下面N行,每行用xi,yi,ti(0≤xi,yi,ti≤10^9)描述了一个弹弓。最后M行用aj和bj描述了需要移动的牛粪。输出格式(文件名:slingshot.out):
输出M行,每堆牛粪一行,表示运输这堆牛粪需要的最短时间。输入样例:
2 3
0 10 1
13 8 2
1 12
5 2
20 7
输出样例:
4
3
10
在这里,第一堆牛粪需要从位置1移动到位置12。不使用弹弓的话,会消耗11单位时间。但是,如果使用第一个弹弓,只需消耗1单位时间移动到位置0(弹弓的发射点),1单位时间将弹弓通过空中弹射并着陆在位置10(弹弓的目的地),然后2单位时间把牛粪移动到位置12。第二堆牛粪的最佳移动方案是不使用弹弓,第三堆牛粪应该使用第二个弹弓。
实现
暴力算法(PHP版本)
<?php
/**
* 牛粪弹弓基类
*/
class SlingshotBase
{
/**
* @var 弹弓数组
*/
public $slingshot_desc = array();
/**
* @var 牛粪数组
*/
public $cow_dung_position = array();
/**
* @var 最小步数数组
*/
public $mininum_steps = array();
/**
* @var 异常信息
*/
public $error = array();
/**
* @var 弹弓数
*/
public $n = 0;
/**
* @var 牛粪数
*/
public $m = 0;
/**
* 构造函数
*/
public function __construct($filename)
{
$contents_from_file = file($filename, FILE_IGNORE_NEW_LINES); //忽略文件末尾的换行符
if (empty($contents_from_file) || count($contents_from_file) < 1) {
throw new Exception('读取文件失败');
}
$n = 0;
$m = 0;
foreach ($contents_from_file as $idx => $line) {
$line_split = preg_split("/\s+/", $line); //多个空格分割
if ($idx < 1) {
$this->_nm($n, $m, $idx, $line_split); //第一行,构建 n, m
} else if ($idx <= $n ) {
$this->_slingshot($idx, $line_split); // 1< i< n,构建弹弓数组
} else if ($idx <= $n + $m ) {
$this->_cowdung($idx, $line_split); // n < i < m,构建牛粪数组
} else {
break; //忽略,其他行
}
}
if (!empty($this->error)) {
throw new Exception(implode("\r\n", $this->error));
}
$this->n = $n;
$this->m = $m;
}
/**
* 构建变量: $n 和 $m
* @param: $n int return
* @param: $m int return
* @param: $idx int 当前行号,异常时给出行号信息
* @param: $line_split = array(int,int)
*/
private function _nm(&$n, &$m, $idx, $line_split)
{
if (count($line_split) != 2) {
array_push($this->error, '第[' . $idx . ']行格式不对: 只能包含n m 2个数字并且以空格分隔');
} else {
$n = $line_split[0];
$m = $line_split[1];
}
}
/**
* 构建类变量: $this->slingshot_desc
* @param: $idx int 当前行号,异常时给出行号信息
* @param: $line_split = array(int,int)