欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

pku acm 2248 addtion chians 解题报告

程序员文章站 2022-05-21 23:29:37
...

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;
}

 

 

 

转载于:https://www.cnblogs.com/rocketfan/archive/2009/06/22/1508212.html