ZOJ2676 动态规划优化

这道题裸的 dp 方程应该可以写的出来,我记录的状态如下:

dp[i][j]dp[i][j] 表示以 aia_i 为第一个元素,aja_j 为第二个元素的斐波那契序列的最大长度,这样 iji \leq j 转移方程:

dp[i][j]=dp[j][k]+1dp[i][j]=dp[j][k]+1

其中 ak=ai+aja_k=a_i+a_j

从后向前扫描,每次更新 dpdp 值后更新最大长度以及第一第二项,最后的答案直接根据第一第二项迭代即可

现在遇到的最大问题就是转移,裸的转移是 O(n)O(n) 的,直接从前向后扫一遍,找到第一个符合条件的 k,这样总的复杂度是枚举加上扫描,是 O(n3)O(n^3) 的,明显对 nn30003000 的数据来说太大了,不可取。

后来想到了二叉树,嗯,就是 map,在树中维护值为 x 的最前位置,这样直接通过在树中找 ai+aja_i+a_j 的最前位置即可,后来想想也不对,复杂度是 O(n2×logn)O(n^2 \times logn) 的,对于 STL 的大常数不放心,算了一下 30002×lg30003000^2 \times lg{3000} 差不多 10810^8 数量级,会 TLE。又开始想转移能不能压缩。

最后想到 hash 的办法,因为转移无非就是在一个序列中找一个 xx 对应的最前位置,直接将 xx 进行 hash,对应的 hash 值的位置存放我们要的 xx 对应的最前位置即可。

hash 函数乱写的 - -

发现其实数据重复的概率不大,没有优化,直接用了。。。

这样可以在近似 O(1)O(1) 的时间内找到我们要的值,转移加上枚举就是 O(n2)O(n^2) 的了

我的代码:

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
97
98
99
100
101
102
103
104
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

const int MAX = 100000;
const int mod = 100007;

struct hbox {
int st;
hbox* s;
} hash[MAX], *h[mod], *cur;
int cnt[mod], pos;

unsigned int BKDHash(int s) {
unsigned int r = abs(s);
return (r * r & 0x7FFFFFFF) % mod;
}

bool has(int s, int& pos) {
int d = BKDHash(s);
hbox* p = h[d];
while (p) {
if (s == p->st) {
pos = p - hash;
return true;
}
p = p->s;
}
return false;
}

void insert(int s, int v) {
int d = BKDHash(s);
hbox* p = h[d];
while (p) {
if (s == p->st) {
cnt[p - hash] = v;
return;
}
p = p->s;
}
cur->st = s;
cur->s = h[d];
h[d] = cur++;
cnt[cur - hash - 1] = v;
}

void init() {
memset(h, 0, sizeof(h));
cur = hash;
}

int a[3100];
short dp[3100][3100];

int main() {
int n;
int x, y, pos;
bool blocks = false;
int ret;
while (~scanf("%d", &n)) {
if (blocks) {
puts("");
} else {
blocks = true;
}
init();
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
dp[i][j] = 1;
}
}
x = a[0];
ret = 0;
for (int j = n - 1; j >= 0; j--) {
for (int i = j - 1; i >= 0; i--) {
if (has(a[i] + a[j], pos)) {
dp[i][j] = dp[j][cnt[pos]] + 1;
}
if (dp[i][j] > ret) {
x = a[i];
y = a[j];
ret = dp[i][j];
}
}
insert(a[j], j);
}
printf("%d\n", ret + 1);
printf("%d", x);
for (int i = 0; i < ret; i++) {
printf(" %d", y);
swap(x, y);
y += x;
}
puts("");
}
return 0;
}