PC/UVa:110607/10049
看前边很多题都需要高精度运算就没做,就捡了一个软的柿子捏,结果这个也不软。
这个序列还是很好递推的,从3开始最好推,也就是build()函数,但是循环还不到最大上上限的一半就已经要用很长时间了,所以肯定不能这么做。
通过观察这个自描述序列,可以发现映射后的值小于映射之前的值,所以可以存储逆映射,而且通过uDebug上的测试,最大值2 000 000 000的函数值也只有673365,这么多项的数组内存还是不会超的。
build2()函数负责构建viFirst这个数组,表示数字num第一次出现的位置,前3项分别初始化为0(无意义)、1(1这个数第一次出现的位置为1)、2(2这个数第一次出现的位置为2),接下来while中第一次加入的数是4,表示3第一次出现的位置为4,然后sum增加序列中3的数量。
更重要的一点是如何在viFirst中找到3的个数,这可以通过在viFirst中查找第一个大于等于3的索引i来计算。如果viFirst中包含3,相应索引为i,表示自描述序列中位置3的值为i,也就是3的个数;如果viFirst中不包含3,但是第一个大于3的值的索引为i,则有viFirst[i - 1] < 3 < viFirst[i],表示数字i - 1第一次出现的位置小于3,而数字i第一次出现的位置大于3,根据序列的性质,位置3的值为i - 1。
如果用O(n)的查找算法来查找个数,又会超时了,所以就又写了二分查找(这又调了好久 ???),这样时间复杂度就从O(n^2)下降到O(nlogn)了。
#include <iostream>
#include <vector>
using namespace std;
void build(vector<int> &viF)
{
viF.push_back(1);
viF.push_back(2);
viF.push_back(2);
for (size_t i = 3; i < 1000000000; i++)
{
for (int j = 0; j < viF[i - 1]; j++)
{
viF.push_back(i);
}
}
}
int find2(vector<int> &viFirst, int ele)
{
size_t begin = 0, end = viFirst.size(), mid;
int ret = -1;
while (end - begin > 1){
mid = (begin + end) >> 1;
if (viFirst[mid] < ele) begin = mid;
else if (viFirst[mid] > ele) end = mid;
else{
ret = mid;
break;
}
}
if (ret == -1){
if (viFirst[end] == ele) ret = end;
else ret = begin;
}
return ret;
}
void build2(vector<int> &viFirst)
{
viFirst.push_back(0);
viFirst.push_back(1);
viFirst.push_back(2);
int sum = 4;
int num = 3;
while (1){
viFirst.push_back(sum);
if (sum >= 2000000000) break;
/*
for (size_t i = 0; i < viFirst.size(); i++)
{
if (num == viFirst[i]){
sum += i;
break;
}
else if (num < viFirst[i]){
sum += i - 1;
break;
}
}
*/
sum += find2(viFirst, num);
num++;
}
}
int main()
{
int n = 0;
vector<int> viF, viFirst;
//build(viF);
build2(viFirst);
//cout << "build up" << endl;
while (cin >> n){
if (n == 0) break;
cout << find2(viFirst, n) << endl;
}
return 0;
}
/*
100
9999
123456
1000000000
*/

被折叠的 条评论
为什么被折叠?



