原题参考:http://www.usaco.org/index.php?page=viewproblem2&cpid=816&lang=zh
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。第二堆牛粪的最佳移动方案是不使用弹弓,第三堆牛粪应该使用第二个弹弓。
$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)
*/
private function _slingshot($idx, $line_split)
{
if (count($line_split) != 3) {
array_push($this->error, '第[' . $idx. ']行格式不对: 只能包含x y t 3个数字并且以空格分隔');
} else {
array_push($this->slingshot_desc, $line_split);
}
}
/**
* 构建类变量:$this->cow_dung_position
* @param: $idx int 当前行号,异常时给出行号信息
* @param: $line_split = array(int,int)
*/
private function _cowdung($idx, $line_split)
{
if (count($line_split) != 2) {
array_push($this->error, '第[' . $idx. ']行格式不对: 只能包含a b 2个数字并且以空格分隔');
} else {
array_push($this->cow_dung_position, $line_split);
}
}
/**
* 步数计算,核心思想是:
* 1. 先计算出不使用弹弓的步数作为基准值 $base
* 2. 依次使用弹弓,分别计算出从起点到弹弓传送门起点和终点所需要的步数,再加上运送弹弓所需要的时间,分别为:$first、$second
* 3. $base, $first, $second 3 者取最小值,并作为新的基准值
* 4. 弹弓遍历完成后,最后的$base 则为最小步数
*/
public function cal()
{
foreach ($this->cow_dung_position as $position) {
$base = abs($position[0] - $position[1]); //基准值,不使用弹弓需要的步数
foreach ($this->slingshot_desc as $slingshot) {
$first = abs($position[0] - $slingshot[0]) + abs($position[1] - $slingshot[1]) + $slingshot[2];
$second = abs($position[0] - $slingshot[1]) + abs($position[1] - $slingshot[0]) + $slingshot[2];
$base = $base < $first ?($base < $second ? $base : $second) : ($first < $second ? $first : $second);
}
array_push($this->mininum_steps, $base);
}
}
/**
* 将类变量参数$this->mininum_steps 写入指定的文件
* @param: $filename string
*/
public function save($filename)
{
file_put_contents($filename, implode("\r\n", $this->mininum_steps));
}
}
try {
//第一种方法:暴力计算
$classname = new SlingshotBase('./slingshot.in');
$classname->cal();
$classname->save('./slingshot.out');
} catch (Exception $e) {
echo $e->getMessage();
}
?>
$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)
*/
private function _slingshot($idx, $line_split)
{
if (count($line_split) != 3) {
array_push($this->error, '第[' . $idx. ']行格式不对: 只能包含x y t 3个数字并且以空格分隔');
} else {
array_push($this->slingshot_desc, $line_split);
}
}
/**
* 构建类变量:$this->cow_dung_position
* @param: $idx int 当前行号,异常时给出行号信息
* @param: $line_split = array(int,int)
*/
private function _cowdung($idx, $line_split)
{
if (count($line_split) != 2) {
array_push($this->error, '第[' . $idx. ']行格式不对: 只能包含a b 2个数字并且以空格分隔');
} else {
array_push($this->cow_dung_position, $line_split);
}
}
/**
* 步数计算,核心思想是:
* 1. 先计算出不使用弹弓的步数作为基准值 $base
* 2. 依次使用弹弓,分别计算出从起点到弹弓传送门起点和终点所需要的步数,再加上运送弹弓所需要的时间,分别为:$first、$second
* 3. $base, $first, $second 3 者取最小值,并作为新的基准值
* 4. 弹弓遍历完成后,最后的$base 则为最小步数
*/
public function cal()
{
foreach ($this->cow_dung_position as $position) {
$base = abs($position[0] - $position[1]); //基准值,不使用弹弓需要的步数
foreach ($this->slingshot_desc as $slingshot) {
$first = abs($position[0] - $slingshot[0]) + abs($position[1] - $slingshot[1]) + $slingshot[2];
$second = abs($position[0] - $slingshot[1]) + abs($position[1] - $slingshot[0]) + $slingshot[2];
$base = $base < $first ?($base < $second ? $base : $second) : ($first < $second ? $first : $second);
}
array_push($this->mininum_steps, $base);
}
}
/**
* 将类变量参数$this->mininum_steps 写入指定的文件
* @param: $filename string
*/
public function save($filename)
{
file_put_contents($filename, implode("\r\n", $this->mininum_steps));
}
}
/**
*/
class TreeArray extends SlingshotBase
{
/**
* @var 对比数据最大值
*/
public $infinite = 10000000000; //可定义成其他
/**
* @var 树形数组个数,弹弓y 和 牛粪 b 数组合并,排序,去重后的数组个数
*/
public $yb = 0;
/**
* @var 树形数组
*/
public $tree_array = array();
/**
* @var 弹弓数组
*/
public $slingshot = array();
/**
* @var 牛粪数组
*/
public $cowdung = array();
/**
* 最大值数组初始化
*/
private function __init_src()
{
$src = array();
$that = clone $this;
for ($i = 1; $i <= $this->yb; $i++) {
$src[$i] = $that->infinite;
}
return $src;
}
/**
* @类变量初始化
*/
public function init()
{
$tree_array = array(); //树形数组
$base_steps = array(); //基准耗时数组
$slingshot = array(array(
'x' => 0,
'y' => 0,
't' => 0,
));
foreach ($this->slingshot_desc as $idx => $shot) {
$idx += 1;
$slingshot[$idx] = array(
'x' => $shot[0],
'y' => $shot[1],
't' => $shot[2],
);
$tree_array[$idx] = $shot[1];
}
$cowdung = array(array(
'a' => 0,
'b' => 0,
'id' => 0,
));
foreach ($this->cow_dung_position as $idx => $position) {
$idx += 1;
$cowdung[$idx] = array(
'a' => $position[0],
'b' => $position[1],
'id' => $idx,
);
array_push($tree_array, $position[1]);
$base_steps[$idx] = abs($position[0] - $position[1]);
}
sort($slingshot); //排序,默认根据数组第一个字段排序
sort($cowdung); // 排序默认根据第一个字段值
$tree_array = array_unique($tree_array);
sort($tree_array);
$that = clone $this;
foreach ($cowdung as &$dung) {
$dung['bb'] = $that->lower_bound($tree_array, $dung['b']) + 1;
}
foreach ($slingshot as $idx => &$sling) {
$sling['yy'] = $that->lower_bound($tree_array, $sling['y']) + 1;
}
$this->cowdung = $cowdung;
$this->slingshot = $slingshot;
$this->tree_array = array_merge($this->tree_array, $tree_array);
$this->mininum_steps = $base_steps;
$this->yb = count($tree_array) ;
$this->tree_array = $tree_array;
}
/**
* 重写父类的计算方法,采用树形数组实现最小值计算和查询
* 对于 |x-a| + |y-b| + t,按照数学公式,分4种情况讨论:
* 1. 对于 x >= a && y >= b, 等价于 x-a+y-b+t = -a-b(固定) + (x+y+t)(树形数组计算)
* 2. 对于 x >= a && y <= b, 等价于 x-a+b-y+t = -a+b(固定) + (x-y+t)(树形数组计算)
* 3. 对于 x < a && y <= b, 等价于 a-x+y-b+ t = a-b(固定) + (-x+y+t)(树形数组计算)
* 4. 对于 x < a && y >= b, 等价于 a-x +b - y + t = a+b(固定) + (-x-y+t)(树形数组计算)
*/
public function cal()
{
$sl = $this->slingshot;
$qr = $this->cowdung;
$m = $this->m;
$n = $this->n;
$nn = $this->yb;
$ans = $this->mininum_steps;
$that = clone $this;
//第一种情况: x <= a && y <= b
$now = 1;
$src = $this->__init_src();
for ($i = 1; $i <= $m; $i++) {
while ($now <= $n && $sl[$now]['x'] <= $qr[$i]['a']) {
$that->add($src, $sl[$now]['yy'], -$sl[$now]['x'] - $sl[$now]['y'] + $sl[$now]['t']);
++$now;
}
$ans[$qr[$i]['id']] = min($ans[$qr[$i]['id']], $qr[$i]['a'] + $qr[$i]['b'] + $that->query($src, $qr[$i]['bb']));
}
//第二种情况: x<=a && y>=b
$now = 1;
$src = $this->__init_src();
for ($i = 1; $i <= $m; $i++) {
while ($now <= $n && $sl[$now]['x'] <= $qr[$i]['a']) {
$that->add($src, $nn - $sl[$now]['yy'] + 1, -$sl[$now]['x'] + $sl[$now]['y'] + $sl[$now]['t']);
++$now;
}
$ans[$qr[$i]['id']] = min($ans[$qr[$i]['id']], $qr[$i]['a'] - $qr[$i]['b'] + $that->query($src, $nn- $qr[$i]['bb'] + 1));
}
//第三种情况: x> a && y < b
$now = $n;
$src = $this->__init_src();
for ($i = $m; $i >= 1; --$i) {
while ($now >= 1 && $sl[$now]['x'] > $qr[$i]['a']) {
$that->add($src, $sl[$now]['yy'], $sl[$now]['x'] - $sl[$now]['y'] + $sl[$now]['t']);
--$now;
}
$ans[$qr[$i]['id']] = min($ans[$qr[$i]['id']], -$qr[$i]['a'] + $qr[$i]['b'] + $that->query($src, $qr[$i]['bb']));
}
//第四种情况: x> a && y> b
$now = $n;
$src = $this->__init_src();
for ($i = $m; $i >= 1; --$i) {
while ($now >= 1 && $sl[$now]['x'] > $qr[$i]['a']) {
$that->add($src, $nn - $sl[$now]['yy'] + 1, $sl[$now]['x'] + $sl[$now]['y'] + $sl[$now]['t']);
--$now;
}
$ans[$qr[$i]['id']] = min($ans[$qr[$i]['id']], -$qr[$i]['a'] +- $qr[$i]['b'] + $that->query($src, $nn - $qr[$i]['bb'] + 1));
}
$this->mininum_steps = $ans;
}
/**
* 使用机器补码特性,计算2 ^ x
* @param: $x int
* @return: int
*/
public function lowbit($x)
{
return $x & -$x;
}
/**
* 树形数组,取最小值
* @param: $src array 最大值数组, 会被修改
* @param: $x int,
* @param: $v int;
*/
public function add(&$src, $x, $v)
{
for (; $x <= $this->yb; $x += $this->lowbit($x)) $src[$x] = min($src[$x], $v);
}
/**
* 树形数组,遍历查询,取出最小值
* @param: $src array 最大值数组
* @param: $x int
*/
public function query($src, $x)
{
$tmp = $this->infinite;
for (; $x ; $x -= $this->lowbit($x)) $tmp = min($tmp, $src[$x]);
return $tmp;
}
/**
* 在源数组中查找指定值,返回大于或等于v的第一个元素位置。如果不存在,则返回数组的最大位置
* @param: $src_array array
* @param: $v mixed
* @return: int
*/
public function lower_bound($src_array, $v)
{
$idx = array_search($v, $src_array);
if ($idx !== false) {
return $idx;
} else {
return count($src_array);
}
}
}
try {
//第一种方法:暴力计算
$classname = new SlingshotBase('./slingshot.in');
$classname->cal();
$classname->save('./slingshot.out');
//第二种方法:树形数组
$tree_array = new TreeArray('./slingshot.in');
$tree_array->init();
$tree_array->cal();
$tree_array->save('slingshot.out.ta');
} catch (Exception $e) {
echo $e->getMessage();
}
?>
#!/usr/bin/python
# -*- coding: utf-8 -*-
from multiprocessing import Pool
#读取文件内容按照指定格式
def file_reader(filename):
with open(filename) as file_object:
contents = file_object.readlines()
line_one = contents[0].split()
n = int(line_one[0])
m = int(line_one[1])
slingshot = []
cowdung = []
for line in contents[1:n+1]:
slingshot.append(line.split())
for line in contents[n+1:n+m+1]:
cowdung.append(line.split())
return slingshot, cowdung
#将list表单数据写入指定文件中
def file_writter(filename, lines):
file = open(filename, 'w')
for line in lines:
file.write(str(line) + '\r\n')
file.close()
#核心算法,暴力计算所有可能
def cal(data):
(i, slingshot, cowdung) = data
steps = []
for cow in cowdung:
base = abs(int(cow[0]) - int(cow[1]))
for shot in slingshot:
t1 = abs(int(cow[0]) - int(shot[0]))
t2 = abs(int(cow[1]) - int(shot[1]))
first = sum([t1, t2, int(shot[2])])
t3 = abs(int(cow[0]) - int(shot[1]))
t4 = abs(int(cow[1]) - int(shot[0]))
second = sum([t3, t4, int(shot[2])])
base = min(base, first, second)
steps.append(base)
return [i, steps]
if __name__ == '__main__':
(slingshot, cowdung) = file_reader('./slingshot.in')
total = len(cowdung)
pools = 5
avg = total / pools
data = [];
#不同进程处理不同的分片数据
start = 0
end = start
for i in range(pools):
start = end
end = start + avg
next = i + 1
if next == pools:
splitcow = cowdung[start:]
else:
splitcow = cowdung[start:end]
data.append([i, slingshot, splitcow])
#多线程池,并将结果按照进程序号排序,避免乱序
p = Pool(pools)
steps = p.map(cal, data)
steps = sorted(steps)
#将计算结果写入指定文件
dstlines = []
for step in steps:
(sn, result) = step
dstlines.extend(result)
file_writter('./slingshot.out', dstlines)