首先说一下DPJuice Extractor
这个DP要完成的就是区间转移,如果以水果为状态的话,无法满足无后效性,把时间离散化,然后用时间点作为状态设计DP。
然后如何转移减少复杂度,朴素的转移需要N^3的复杂度,方法为,计算第i时间切一道枚举j = (i - 1 至0)为前一刀,然后O(N)的计算区间内有多少的水果。
我们记录每个时间点之前出现的水果的位置,然后从该位置开始向前枚举于枚举迁移刀的时间同步,并且记录在这时间的水果结束时间大于等于i的个数,这样一来时间复杂度被均摊了……具体代码如下
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <iterator>
#include <limits>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <climits>
#include <algorithm>
#include <functional>
#include <numeric>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <bitset>
#include <list>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <stdexcept>
#include <utility>
#include <cassert>
#include <complex>
using namespace std;
#define LEFT(i) ((i) << 1)
#define RIGHT(i) (((i) << 1) | 1)
#define MID(i) ((l[i] + r[i]) >> 1)
#define CC(i, v) memset(i, v, sizeof(i))
#define REP(i, l, n) for(int i = l;i < int(n);++i)
#define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)
typedef long long LL;
const int N = 2010;
struct interval
{
int begin, end;
bool operator < (const interval& a)const
{
return begin < a.begin;
}
}fruit[N];
vector<int> times;
int dp[N], before[N];
int main()
{
int n, m, t;
scanf("%d", &t);
for(int k = 1;k <= t;k++)
{
scanf("%d", &n);
times.clear();
for(int i = 0;i < n;i++)
{
scanf("%d %d", &fruit[i].begin, &fruit[i].end);
times.push_back(fruit[i].begin);
times.push_back(fruit[i].end);
}
times.push_back(-1);
sort(fruit, fruit + n);
sort(times.begin(), times.end());
times.erase(unique(times.begin(), times.end()), times.end());
memset(before, -1, sizeof(before));
m = times.size();
for(int i = 0;i < m;i++) for(int j = 0;j < n;j++)
if(fruit[j].begin <= times[i])
before[i] = j;
else break;
memset(dp, 0, sizeof(dp));
for(int i = 0;i < m;i++)
{
int cnt = 0;
for(int j = i - 1;j >= 0;j--)
{
while(before[i] >= 0 && fruit[before[i]].begin > times[j])
{
cnt += fruit[before[i]].end >= times[i];
before[i]--;
}
if(cnt > 2)
dp[i] = max(dp[i], dp[j] + cnt);
else
dp[i] = max(dp[i], dp[j]);
}
}
printf("Case #%d: %d\n", k, dp[m - 1]);
}
return 0;
}
然后是以后字符串处理的题目,直观思想是后缀数组,KMP会更简单一些(起码写起来更简单)。
题目要求求出一个长度n的字符串的最长的1重复字串到n重复字串
记录一下后缀数组的解法:
首先求出后缀数组,构建height于rmq(不会后缀数组的同学请参考罗某的论文)。
我的做法是枚举以i开头的后缀,再枚举一个区间长度j, 求出i与i + j的lcp = a,然后更新从a / j + 1 to 1的所有ans。
思想很直观a = lcp(i, i + j);那么必然有长度为 (a / j + 1) * j的都是重复的字串比他小的如(a / j )*j的自然也是。所以一直更新即可。
在最后更新的这个操作上,由于求的是a / j所以复杂度是logN的,整体复杂度是N^2logN。
乘以cases数后,差不多刚好能过……
代码如下
1 /*
2 * =====================================================================================
3 *
4 * Filename: sfx.cpp
5 *
6 * Description:
7 *
8 * Version: 1.0
9 * Created: 08/18/2011 09:36:17 AM
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: ronaflx
14 * Company: hit-ACM-Group
15 *
16 * =====================================================================================
17 */
18 #include <cstdlib>
19 #include <cctype>
20 #include <cstring>
21 #include <iterator>
22 #include <limits>
23 #include <cstdio>
24 #include <cmath>
25 #include <ctime>
26 #include <climits>
27 #include <algorithm>
28 #include <functional>
29 #include <numeric>
30 #include <vector>
31 #include <map>
32 #include <set>
33 #include <queue>
34 #include <stack>
35 #include <bitset>
36 #include <list>
37 #include <string>
38 #include <iostream>
39 #include <sstream>
40 #include <fstream>
41 #include <iomanip>
42 #include <stdexcept>
43 #include <utility>
44 #include <cassert>
45 #include <complex>
46 using namespace std;
47
48 #define LEFT(i) ((i) << 1)
49 #define RIGHT(i) (((i) << 1) | 1)
50 #define MID(i) ((l[i] + r[i]) >> 1)
51 #define CC(i, v) memset(i, v, sizeof(i))
52 #define REP(i, l, n) for(int i = l;i < int(n);++i)
53 #define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)
54
55 typedef long long LL;
56 const int MAXN = 21000;
57 struct Sfx
58 {
59 int i;
60 int key[2];
61 bool operator < (const Sfx& s) const
62 {
63 return key[0] == s.key[0] ? key[1] < s.key[1] : key[0] < s.key[0];
64 }
65 } sfx[MAXN], temp[MAXN];
66 int rank[MAXN], bucket[MAXN], height[MAXN];// rank from 0 to n - 1
67 //基数排序,先拍第二关键字,再第一关键字
68 void radixSort(Sfx* in, int n, int idx, Sfx* out)
69 {
70 memset(bucket, 0, sizeof(int) * (n + 1));
71 for (int i = 0; i < n; i++)
72 bucket[in[i].key[idx]]++;
73 for (int i = 1; i <= n; i++)
74 bucket[i] += bucket[i - 1];
75 for (int i = n - 1; i >= 0; i--)//for down
76 out[--bucket[in[i].key[idx]]] = in[i];
77 }
78 void buildSA(const char* text, int n)
79 {
80 for (int i = 0; i < n; i++)
81 {
82 sfx[i].i = sfx[i].key[1] = i;
83 sfx[i].key[0] = text[i];
84 }
85 sort(sfx, sfx + n);
86 for (int i = 0; i < n; i++)//下面要比较,所以全变为0
87 sfx[i].key[1] = 0;
88 int wid = 1;
89 while (wid < n)
90 {
91 rank[sfx[0].i] = 0;
92 for (int i = 1; i < n; i++)
93 rank[sfx[i].i] = rank[sfx[i - 1].i] + (sfx[i - 1] < sfx[i]);
94 for (int i = 0; i < n; i++)
95 {
96 sfx[i].i = i;
97 sfx[i].key[0] = rank[i];
98 sfx[i].key[1] = i + wid < n ? rank[i + wid]: 0;
99 }
100 radixSort(sfx, n, 1, temp);
101 radixSort(temp, n, 0, sfx);
102 wid <<= 1;
103 }
104 }
105 void calHeight(const char* text, int* rank, int n)
106 {//h[i] = height[rank[i]], h[i] >= h[i - 1] - 1;
107 for(int i = 0;i < n;i++)
108 rank[sfx[i].i] = i;
109 for(int i = 0, k = 0, j; i < n; i++)
110 {
111 if (rank[i] == 0)
112 height[rank[i]] = 0;
113 else
114 {
115 if(k > 0) k-- ;
116 for (j = sfx[rank[i] - 1].i; text[i + k] == text[j + k];k++);
117 height[rank[i]] = k;
118 }
119 }
120 }
121 int RMQ[MAXN][20];
122 void buildRMQ(int n, int* height)
123 {
124 for(int i = 1;i <= n;i++) RMQ[i][0] = height[i - 1];
125 for (int j = 1; j <= log(n + 0.00) / log(2.0); j++)
126 for (int i = 1; i + (1 << j) - 1 <= n; i++)
127 RMQ[i][j] = min(RMQ[i][j - 1], RMQ[i + (1 << (j - 1))][j - 1]);
128 }
129 int queryRMQ(int a, int b)
130 {
131 int len = log(b - a + 1.0) / log(2.0);
132 return min(RMQ[a][len], RMQ[b - (1 << len) + 1][len]);
133 }
134 int queryLCP(int a, int b)
135 {
136 a = rank[a] + 1;
137 b = rank[b] + 1;
138 if(a > b) swap(a, b);
139 return queryRMQ(a + 1, b);
140 }
141
142 char str[MAXN];
143 int ans[MAXN];
144 int main()
145 {
146 int t;
147 scanf("%d", &t);
148 for(int k = 1;k <= t;k++)
149 {
150 scanf("%s", str);
151 int n = strlen(str);
152 buildSA(str, n);
153 calHeight(str, rank, n);
154 buildRMQ(n, height);
155 memset(ans, 0, sizeof(ans));
156 ans[1] = n;
157 for(int i = 0;i < n;i++)
158 {
159 int len = n - i;
160 for(int j = 1;j < len;j++)
161 {
162 int tmp = queryLCP(i, i + j);
163 int c = tmp / j + 1;
164 for(int cc = 1;cc <= c;cc++)
165 ans[cc] = max(ans[cc], cc * j);
166 }
167 }
168 printf("Case #%d: ", k);
169 for(int i = 1;i <= n;i++)
170 printf("%d%c", ans[i], i == n ? '\n' : ' ');
171 }
172 return 0;
173 }