zoukankan      html  css  js  c++  java
  • pku acm 2248 addtion chians 解题报告

    acm  2248
    给定n,找最小的序列。
    a0 = 1
    am = n

    a0 < a1 < a2 ... < am

    任意 ak = al1 + al2.

    l1 l2 可以相等

    如 n = 5
    找到 1 2 3 5
    或者 12 4 5 都可 输出一个即可

    但是 12 3 4 5 就不是最短的了

    开始问题没想好,总是超时,参考了

    http://hi.baidu.com/ecjtuzdh/blog/item/ea5d2c025ba7c381d53f7ce9.html

    提到问题的关键

    对于1<=P<=k (a[k]=n) 有a[p]=a[p-1]+a[t] ,t为[0,p-1]中的一个数

    刚开始其实也有想到,但是总觉得没有完全证明,对于a[k]最后一个数这个结论肯定成立,否则序列可更短,但是对于序列中前面的数呢。

    1 , 2, 4, 8, 10

    1, 2 , 4 , 8 , 16

     问题是 1, 2, 4 , 8 ,10 ,16, 26这个序列要不要被考虑搜索呢?

    开始觉得可能需要因为26直接由10,16计算来比较快到达26.

    但其实

    1  2  4  8 16 24 26是一样的
    16 = u + a u = 8 a = 8
    10 = u + b u = 8 b = 2
    然后 1 2 4  u u+a u + u + a  u + u + a +b一样能取到26长度不会更长。
    所以证明成功,因为后面的序列如果之和10右关系则1,2 , 8 10可取代,如果只和16
    有关系则1,2,4,8,16可以取代,而26的情况前面分析了也可以取代。所以,能剪掉很多枝。

    另外可以用归纳法证明
    对于到达任意数字的最短路径。
    当路径长度为2的时候
    a1 a2  (a2 = a1 + a1)
    显然成立
    假设对于路径长度为n的到任意数字的最短路径成立,
    那么对于n+1的最短路径
    如果不成立,即对an+1不符合归则
    则 a(n+1) = ai + aj; (i < n-1, j < n-1)
    则到a(n+1)存在序列 a1, a2   ai   aj an+1符合要求
    所以与原序列是最短路径矛盾,得证。

    这个证明的正确性不是很确定,呵呵,总之是可以剪枝了,不需要考虑那种情况了。

    另外搜素的时候从大数字的分支优先搜索速度较快,因为大数字的分支内容较少,且容易较快的到达目的数字。

    代码如下

    恩,发现用g++速度比c++慢很多0M->16M,恩以后都用c++提交:)

     这个题目也可以用BFS求解,16Ms,用open close表记录已经出队列的元素信息即可,需要的空间比较大。

    //DFS 算法

    #include <iostream>
    using namespace std;
    const int MaxNum = 100;
    int a[MaxNum];     //递归中的当前扫描路径
    int b[MaxNum];     //记录当前找到的最短路径
    void PrintPath(int n) {
    for (int i = 0; i <= n; i++)
    cout << b[i] << " ";
    cout << endl;
    }
    int shortest_len = MaxNum;
    void FindShortestChian(int n, int i) {
    for (int j = i - 1; j >= 0; j--) {
    int sum = a[i - 1] + a[j];
    if (sum == n) {
    a[i] = n;
    for (int k = 0; k <= i; k++ )
    b[k] = a[k];
    shortest_len = i;
    return;
    } else if (sum < n && i + 1 < shortest_len) {
    a[i] = sum;
    FindShortestChian(n, i+1);
    }
    }
    }
    int main(int argc, char *argv[])
    {
    int n;
    a[0] = 1;
    a[1] = 2;
    b[0] = 1;
    b[1] = 2;
    while (1) {
    cin >> n;
    if (n == 0)
    break;
    else if (n == 1 || n == 2)
    PrintPath(n - 1);
    else {
    shortest_len = MaxNum;
    FindShortestChian(n, 2);
    PrintPath(shortest_len);
    }
    }
    return 0;
    }

     //BFS算法

    #include <iostream>
    #include <stack>
    using namespace std;
    int Data[9000000];
    int PreIndex[9000000];
    //close 指向当前要处理的队列元素,close前即为已经出队列的
    //open指向队列尾部,即新加入元素要插入的位置
    //close == open 表示队列为空了
    //data 数组存储队列元素数值
    //index 数组存储对应队列元素的前驱元素的下标索引
    void PrintPath(int close, int n) {
    stack<int> s;
    s.push(n);
    for (int i = close; i != -1; i = PreIndex[i])
    s.push(Data[i]);
    while (!s.empty()) {
    cout << s.top() << " ";
    s.pop();
    }
    cout << endl;
    }
    void FindShortestChian(int n) {
    Data[0] = 1;
    PreIndex[0] = -1;
    Data[1] = 2;
    PreIndex[1] = 0;
    int close = 1;
    int open = 2;
    while (close != open) {
    int a = close;
    int sum;
    while (a != -1){
    sum = Data[close] + Data[a];
    if (sum == n) {
    PrintPath(close, n);
    return;
    } else if (sum < n) {
    Data[open] = sum;           //入队列
    PreIndex[open++] = close;   //存前驱索引为当前处理元素index,open++
    }
    a = PreIndex[a];
    }
    close++;    //当前元素处理完,出队列
    }
    }
    int main(int argc, char *argv[])
    {
    int n;
    while (1) {
    cin >> n;
    if (n == 0)
    break;
    if (n == 1)
    cout << "1" << endl;
    else if (n == 2)
    cout << "1 2" << endl;
    else
    FindShortestChian(n);
    }
    return 0;
    }
  • 相关阅读:
    输入/输出的格式和方法
    程序编译运行和exe运行之文件位置的区别
    ZOJ_3950_How Many Nines 解题报告及如何对程序进行测试修改
    [Offer收割]编程练习赛13 解题报告
    查找语言自带函数
    codeblocks下的汇编语言
    hiho一下 第144周(机会渺茫)解题报告及拓展
    关闭调试窗口快捷方式
    编写程序一个位置的快速到达和修改
    poj3660(Cow Contest)解题报告
  • 原文地址:https://www.cnblogs.com/rocketfan/p/1508212.html
Copyright © 2011-2022 走看看