月度归档:2010年06月

垃圾陷阱

问题描述
【Description】

卡门——农夫约翰极其珍视的一条Holsteins奶牛——已经落了到“垃圾井”中。“垃圾井”是农夫们扔垃圾的地方,它的深度为D (2 <= D <= 100)英尺。 卡门想把垃圾堆起来,等到堆得与井同样高时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。 每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。 假设卡门预先知道了每个垃圾扔下的时间t(0a then a:=b。

  这道题可以用DP解决。用l[i,j]表示掉下来i个垃圾后,卡门爬上的高度为j时她最长的寿命。显然l[0,0]=10。对于任一个状态l[i-1,j],若l[i-1,j]>=g[i].time,说明这个状态的卡门能够坚持到下一个垃圾下落。在这种情况下,按以下两种方法处理第i个垃圾,即进行状态转移:

吃掉第i个垃圾,即update(l[i,j],l[i-1,j]+g[i].life);

用第i个垃圾来垫高。令t=j+g[i].height,即把第i个垃圾用来垫高后卡门爬上的总高度。如果t>=d,则卡门于g[i].time时爬了出来,否则update(l[i,t],l[i-1,j])。

  若首次遇到某一个l[i,0]一次也没有赋值,说明卡门不可能坚持到第i个垃圾下落,则她最多可以存活的时间为l[i-1,0](即把前i-1个垃圾全吃掉后的寿命)。

  注意到在计算l数组的第i行时只用到了第i-1行,因此l数组可用滚动数组来实现。

【代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <stdio.h>
#include <string.h>
 
#define MAXN 101
#define max(a,b) a>b?a:b
 
typedef struct node
{
       int time, life, height;
}node;
 
int main()
{
 
       node a[MAXN], temp;
       int flag;
 
       int f[MAXN][MAXN * 10];
       int n, d, i, j, k, t, maxt, m;
 
       scanf("%d%d", &d, &n);
       maxt = 0;
 
       for (i = 1; i <= n; i++)
       {
              scanf("%d%d%d", &a[i].time, &a[i].life, &a[i].height);
       }
 
       for (i = 1; i < n; i++)
       {
 
              for (j = i + 1; j <= n; j++)
              {
 
                     if (a[i].time > a[j].time)                                                                 
                     {
                            temp = a[i];
                            a[i] = a[j];
                            a[j] = temp;
 
                     }
              }
       }
 
       memset(f, 0, sizeof(f));
 
       f[0][0] = 10;
       flag = 0;
       maxt = 0;
 
       for (i = 1; i <= n; i++)
       {
 
              m = 0;
              k = 0;
 
              for (j = 0; j <= maxt; j++)
              {
 
                     if (f[i - 1][j] >= a[i].time)
                     {
 
                            f[i][j] = max(f[i][j], f[i - 1][j] + a[i].life);
                            t = j + a[i].height;
 
                            if (t >= d)
                            {
                                   flag = a[i].time;
                                   break;
                            }
 
                            if (t > m)
                                   m = t;
 
                            f[i][t] = max(f[i][t], f[i - 1][j]);
                            k++;
                     }
              }
 
              if (k == 0)
                     break;
 
              if (flag != 0)
                     break;
 
              maxt = m;
       }
 
       if (flag != 0)
              printf("%d\n", flag);
       else
       {
              printf("%d\n", f[i - 1][0]);
       }
       return 0;
}

应该是5年前的代码了,从以前的百度blog中转了过来

PHP设计模式笔记:使用PHP实现原型模式

PHP设计模式笔记:使用PHP实现原型模式

【意图】
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

【原型模式结构图】
Prototype

【原型模式中主要角色】
抽象原型(Prototype)角色:声明一个克隆自身的接口

具体原型(Concrete Prototype)角色:实现一个克隆自身的操作

【原型模式的优点和缺点】
Prototype模式优点:
1、可以在运行时刻增加和删除产品
2、可以改变值以指定新对象
3、可以改变结构以指定新对象
4、减少子类的构造
5、用类动态配置应用

Prototype模式的缺点:
Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。
而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。

【原型模式适用场景】
1、当一个系统应该独立于它的产品创建、构成和表示时,要使用Prototype模式
2、当要实例化的类是在运行时刻指定时,例如动态加载
3、为了避免创建一个与产品类层次平等的工厂类层次时;
4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些

【原型模式与其它模式】
抽象工厂模式(abstract factory模式):Abstract Factory模式与Prototype模式在某种方面是相互竞争的。但是也可以一起使用

【原型模式PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
 
<?php
/**
 * 原型模式 2010-06-27 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
/**
 * 抽象原型角色
 */
interface Prototype {
    public function copy();
}
 
/**
 * 具体原型角色
 */
class ConcretePrototype implements Prototype{
 
    private  $_name;
 
    public function __construct($name) {
        $this->_name = $name;
    }
 
    public function setName($name) {
        $this->_name = $name;
    }
 
    public function getName() {
        return $this->_name;
    }
 
    public function copy() {
       /* 深拷贝实现
        $serialize_obj = serialize($this);  //  序列化
        $clone_obj = unserialize($serialize_obj);   //  反序列化                                                     
        return $clone_obj;
        */
        return clone $this;     //  浅拷贝
    }
}
 
/**
 * 测试深拷贝用的引用类
 */
class Demo {
    public $array;
}
 
class Client {
 
     /**
     * Main program.
     */
    public static function main() {
 
        $demo = new Demo();
        $demo->array = array(1, 2);
        $object1 = new ConcretePrototype($demo);
        $object2 = $object1->copy();
 
        var_dump($object1->getName());
        echo '<br />';
        var_dump($object2->getName());
        echo '<br />';
 
        $demo->array = array(3, 4);
        var_dump($object1->getName());
        echo '<br />';
        var_dump($object2->getName());
        echo '<br />';
 
    }
 
}
 
Client::main();
?>

【浅拷贝与深拷贝】

浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。
即 浅拷贝只负责当前对象实例,对引用的对象不做拷贝。

深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。
即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。
深拷贝要深入到多少层,是一个不确定的问题。
在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。
因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。

利用序列化来做深拷贝
利用序列化来做深拷贝,把对象写到流里的过程是序列化(Serilization)过程,但在业界又将串行化这一过程形象的称为“冷冻”或“腌咸菜”过程;
而把对象从流中读出来的过程则叫做反序列化(Deserialization)过程,也称为“解冻”或“回鲜”过程。
在PHP中使用serialize和unserialize函数实现序列化和反序列化

在上面的代码中的注释就是一个先序列化再反序列化实现深拷贝的过程

百度招聘的一道题

在烂叶的blog上看到一篇关于百度招聘的一道题
地址:百度招聘的一道试题

有些兴趣,自己实现了下,使用了类蒙地卡罗方法实现。

【题目】
2009百度实习笔试题
要求用PHP,shell或c完成
输入:N(整数)
输入:数据文件A.txt,不超过6条记录,字符串长度不超过15个字节
文件格式如下:
字符串\t数字\n

说明:
每行为1条记录;字符串中不含有\t。
数字描述的是该字符串的出现概率,小于等于100的整数。
多条记录的出现概率之和为100,如果A.txt不满足该条件,程序则退出;
如果文件格式错误,程序也退出。

要求:
编写一个程序,输入为N(正整数),读入文件A.txt,按照字符串出现概率随机地输出字符串,输出N条记录

例如:
输入文件A.txt
abc20
a30
de50
输入为:10

即 abc有20%的概率输出,a有30%的概率输出,de有50%的概率输出,输出10条记录
以下为一次输出的结果,多次输出的结果可能不相同。
abc
a
de
de
abc
de
a
de
a
de

【算法】
先将数据从文件中取出存放到数组,然后将概率空间分割为所给字符串个数个部分,然后生成N个1-100的随机数,然后看每个随机数落在哪一个区间,就将该区间对应的字符串输出。

【程序】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 
$filename = "A.txt";
 
/**
* 数据初始化 读取文件内容,拆分字符串形成数据,
* @param <type> $filename  数据文件名
* @return <type> 如果文件打开错误或文件错误返回FALSE                                                      
*/
function data_init($filename) {
    $lines = file($filename);
 
    if ($lines === FALSE) {
       return FALSE;
    }
 
    $data = array();
    foreach ($lines as $row) {
       $row = rtrim($row, "\r\n");
 
       $index = strpos($row, "\t");
       $key = substr($row, 0, $index);
 
       if (strlen($key) > 15) {
           return FALSE;
       }
 
       $data[$key] = (int) substr($row, $index + 1);
    }
 
    return $data;
}
 
$data = data_init($filename);
 
if ($data === FALSE) {  //  输入文件错误
    die('Input File Error!');
}
 
if (array_sum($data) != 100) {  //  输入数据错误
    die('Input Data Error!');
}
 
/* 以类蒙地卡罗方法实现 */
$map = array();
$k = 0;
 
/* 概率空间分割 */
foreach ($data as $key => $row) {                                                 
    $i = $row;
    for ($i = 1; $i <= $row; $i++) {
       $map[$k + $i] = $key;
    }
    $k += $row;
}
 
/* 随机输出 */
$count = 10;
for ($i = 0; $i < $count; $i++) {
    $rand_num = rand(1, 100);
    echo $map[$rand_num], '<br />';
}

关于蒙特卡洛方法请猛击:蒙特卡洛方法