|
题目大意: 给出一个序列,长度为N,均为正数。 找出一段连续的区间,此区间的平均值最大,长度必须大于F。
好像还是有点实际用途的,这个问题。 看完题之后,基本上就知道是做不出来的了。只想得到那种最简单的O(N^2)的解法,但是N = 100,000。这种解法必然超时。
在网上搜了两个解题报告,发现此题的解法相当牛逼! 两种解法是完全不同类型的。
二分法 我们可以比较容易得出答案的最大值和最小值,即为序列中最大元素和最小元素。 二分法的关键在于判断“一个可能的解跟正确答案相比是大了还是小了”。网上给的方法是: 如果要判断val这个解,那就让序列里所有元素的值都减去val。 然后试图寻找一段连续的区间,该区间的长度大于F,并且区间大于0。 可见,问题一下转化成统计数字的和,而不是数字的平均值,问题变得明朗了。 寻找这种区间的算法是一个很简单的动态规划,复杂度为O(N)。 用 f[a, b] 表示在区间 [a, b] 中,所有子区间的最大值。 那么 当 b - a = F 时,f[a, b] 为序列中对应的和。 当 b - a > F 时,f[a, b] = max{ f[a, b - 1] + arr[b], f[b - f + 1, b] }
我们要求的是 f[0, N]。 因此,二分法的复杂度是 O(NlgN)。代码跑了接近300ms。
/**//* * 代码大量参考这份解题报告 * http://blog.sina.com.cn/s/blog_5c95cb070100dd47.html * 原作者代码写得很不错!赞一个! */ #include <stdio.h>
#define MAX_N 100032
double S[MAX_N], A[MAX_N]; int N, F;
int check(double val) { double cur, pre; int i;
pre = S[F - 1] - val * (F - 1); for (i = F; i <= N; i++) { cur = S[i] - S[i - F] - val * F; pre = pre + A[i] - val; if (cur > pre) pre = cur; if (pre > -1e-6) return 1; }
return 0; }
int main() { int i; double l, r, m;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &N, &F); l = 1e50; r = 0; A[0] = S[0] = 0; for (i = 1; i <= N; i++) { scanf("%lf", &A[i]); if (A[i] > r) r = A[i]; if (A[i] < l) l = A[i]; S[i] = S[i - 1] + A[i]; }
while (r - l >= 1e-6) { m = (l + r) / 2; if (check(m)) l = m; else r = m; }
printf("%d\n", (int)(r * 1000));
return 0; }
凸包法
这个方法不是真的求点的凸包,是用了求凸包时候的技巧。 首先把序列转化成一个图,一共有N个点,第 i 个点的坐标为 (i, S[i]),其中 S[i] 为序列的前 i 项和。 在图上,能观察到,点a点b之间的斜率就是区间[a, b]的平均值。 当 N = 6, F = 3 的时候,按照最简单的 O(N^2) 的做法,计算每两个点之间的斜率,计算的顺序为: [1, 3] [1, 4] [2, 4] [1, 5] [2, 5] [3, 5] [1, 6] [2, 6] [3, 6] [4, 6] 在算第6个点的时候,依次算了1,2,3,4跟点6的斜率。 为了避免不必要的计算,我们要没必要计算的点剔除。 用类似凸包的计算更新方法,在点1,2,3。。。中维护一条“下凸折线”。 这样,可以保证末尾的点跟折线中的点的斜率是先递增再递减的关系。 就能比较快的找出最大的斜率了。 这个算法的复杂度,网上的人说是O(N),但我觉得好像不是O(N)啊,也不知道是什么。 但是,绝对不能单单以复杂度来评价算法的啦。 代码跑了150ms左右。比2分的还是快一点。
/**//* * 思路参考此解题报告 * http://hi.baidu.com/ultramanzhy/blog/item/a8cb4efa1ecf2e1aa9d31123.html * 解法牛逼!赞一个! */ #include <stdio.h>
#define MAX_N 100032
int S[MAX_N], stack[MAX_N], N, F, sp;
__inline int turn_right(int a, int b, int c) { int x1, y1, x2, y2;
x1 = b - a; y1 = S[b] - S[a]; x2 = c - b; y2 = S[c] - S[b];
return x1*y2 - x2*y1 <= 0; }
__inline double calc_k(int a, int b) { return (double)(S[b] - S[a]) / (double)(b - a); }
int main() { int i, j; double max_val, val;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &N, &F); for (i = 1; i <= N; i++) { scanf("%d", &j); S[i] = S[i - 1] + j; } max_val = 0; for (i = 0; i <= N - F; i++) { while (sp >= 2 && turn_right(stack[sp - 2], stack[sp - 1], i)) sp--; stack[sp++] = i; for (j = sp; j >= 2 && turn_right(stack[j - 2], stack[j - 1], i + F); j-- ); val = calc_k(stack[j - 1], i + F); if (val > max_val) max_val = val; } printf("%d\n", (int)(max_val * 1000));
return 0; }
思路: 事先计算出第1500个数为859963392,枚举2,3,5的所有乘积,过滤掉所有大于859963392的数。 积攒到1500个的时候停止计算。然后快排就行了。速度还挺快呢。0ms。
#include <stdio.h> #include <stdlib.h>
#define MAX_N 859963392
__int64 arr[1501]; int cnt;
int cmp(const void *a, const void *b) { return *(__int64 *)a - *(__int64 *)b; }
int main() { __int64 a, b, c; int i;
/**//* cnt = 0; for (i = 1; cnt < 1500; i++) { val = i; while (!(val & 1)) val >>= 1; while (!(val % 3)) val /= 3; while (!(val % 5)) val /= 5; if (val == 1) { printf("#%d %d\n", cnt, i); cnt++; } } */ for (a = 1; a <= MAX_N; a *= 2) { for (b = 1; a*b <= MAX_N; b *= 3) { for (c = 1; a*b*c <= MAX_N; c *= 5) { arr[++cnt] = a*b*c; if (cnt >= 1500) goto done; } } }
done: qsort(arr + 1, 1500, sizeof(arr[0]), cmp);
while (scanf("%d", &i), i) printf("%d\n", arr[i]);
return 0; }
思路: 每个节点记录两个值: 所有子节点的增量和 该节点的增量 代码烂,1500+ms。 为了避免爆栈,实现计算好了左右边界和中间值。
#include <stdio.h> #include <stdlib.h>
#define MAX_N 100032
int N; struct { __int64 up, down; int rl, rr, rm; } tree[MAX_N*4];
enum OP { INSERT, SUM, } op;
__int64 val;
void tree_op(int idx, int l, int r) { if (op == INSERT) { if (tree[idx].rl == l && tree[idx].rr == r) { tree[idx].up += val; return ; } tree[idx].down += val * (r - l + 1); } else { val += tree[idx].up * (r - l + 1); if (tree[idx].rl == l && tree[idx].rr == r) { val += tree[idx].down; return ; } }
if (r <= tree[idx].rm) { // all in left tree_op(idx*2, l, r); } else if (l > tree[idx].rm) { // all in right tree_op(idx*2+1, l, r); } else { // in left and right tree_op(idx*2, l, tree[idx].rm); tree_op(idx*2+1, tree[idx].rm + 1, r); } }
int main() { int i, j, q; char str[16];
freopen("e:\\test\\in.txt", "r", stdin);
tree[1].rl = 1; tree[1].rm = (MAX_N + 1) / 2; tree[1].rr = MAX_N; for (i = 2; i < _countof(tree); i++) { if (i & 1) { tree[i].rl = tree[i/2].rm + 1; tree[i].rr = tree[i/2].rr; } else { tree[i].rl = tree[i/2].rl; tree[i].rr = tree[i/2].rm; } tree[i].rm = (tree[i].rl + tree[i].rr) / 2; }
scanf("%d%d", &N, &q); op = INSERT; for (i = 1; i <= N; i++) { scanf("%I64d", &val); tree_op(1, i, i); }
while (q--) { scanf("%s%d%d", str, &i, &j); if (str[0] == 'Q') { val = 0; op = SUM; tree_op(1, i, j); printf("%I64d\n", val); } else { scanf("%I64d", &val); op = INSERT; tree_op(1, i, j); } }
return 0; }
题目大意: 给出一个01字符串。将其循环移位,将所有移位后的串列举出来,并按字典序排序。 比如“abcd”,可以移位成“bcda”,“cdab”,“dabc”。排序以后,为 “abcd” “bcda” “cdab” “dabc” 将最后一列的字母抽取出来,即“dabc”。 然后,让你还原出第一行的字符。 这是一个看上去很蛋疼的问题,没事研究这个干啥呢。 想了半天,做不出来。看到discuss里面有人给了一个链接: http://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform发现居然是一个压缩算法! 据说,将最后一列抽取出来,较原字符串相比,能够 “easy to compress a string that has runs of repeated characters by techniques such as move-to-front transform and run-length encoding.” 这个就不理解了。。 这题简化了一下条件,字符串只包含01。 看了它的还原方法。如果直接这样写:
def ibwt(r):
"""Apply inverse Burrow-Wheeler transform."""
table = [""] * len(r) # Make empty table
for i in range(len(r)):
table = [r[i] + table[i] for i in range(len(r))] # Add a column of r
table.sort()
s = [row for row in table if row.endswith("\0")][0] # Find the correct row (ending in "\0")
return s.strip("\0") # Get rid of trailing null character
还是复杂度很高,为 O(N*N*lgN)。 那disscus里面说的O(N)的方法和那么多0ms是咋来的呢? 想了一下。发现每一次添加列然后再排序的操作,都是做一样的置换。 因为每次添加的列都一样,排序的结果当然也是一样(比如是稳定排序)。 所以,第i列的结果就是置换i次的结果。我们只需要第一行的数据。 经过一次排序之后,就知道每一个行置到了哪个地方,如果第三行到了第一行,第五行到了第三行。 那么: 第一列第一行的就是未排序数据的第三行 第二列第一行的就是未排序数据的第五行 由于数据中只有01,完全可以在O(N)内完成排序操作,之后得出第一行的操作也是 O(N) 完成的。 可惜代码实现出来,也没有到 0ms ,好像是 79ms 。代码写得烂!高手改改也是0ms了。 代码里也包括了一个求普通字符串的BWT操作。
#include <stdio.h> #include <stdlib.h> #include <string.h>
int cmp(const void *a, const void *b) { return strcmp(*(char **)a, *(char **)b); }
void bwt(char *in, char *out) { static char arr[32][32], *sorted[32]; int len, i, j;
len = strlen(in); for (i = 0; i < len; i++) { sorted[i] = arr[i]; for (j = 0; j < len; j++) sorted[i][j] = in[(i + j) % len]; sorted[i][len] = 0; }
qsort(sorted, len, sizeof(sorted[0]), cmp); for (i = 0; i < len; i++) printf("%s\n", sorted[i]);
for (i = 0; i < len; i++) out[i] = sorted[i][len - 1]; out[i] = 0; }
int cmp2(const void *a, const void *b) { if (*(int *)a == *(int *)b) return *((int *)a + 1) - *((int *)b + 1); else return *(int *)a - *(int *)b; }
void ibwt(char *in, char *out) { struct { int ch, idx; } arr[32]; int i, len, j;
len = strlen(in); for (i = 0; i < len; i++) { arr[i].ch = in[i]; arr[i].idx = i; } qsort(arr, len, sizeof(arr[0]), cmp2); for (i = 0; i < len; i++) printf("%c %d\n", arr[i].ch, arr[i].idx); j = arr[0].idx; for (i = 0; i < len - 1; i++) { out[i] = in[j]; j = arr[j].idx; } out[len - 1] = in[0]; out[len] = 0; printf("%s\n", out); }
void test() { char in[32], out[32], res[32];
strcpy(in, "banana"); bwt(in, out); printf("out:\n%s\n", out); ibwt(out, res); }
int main() { static int map[3032], arr[3032], n, val, one[3032], one_cnt, zero_cnt, i; freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%d", &val); arr[i] = val; if (val) one[one_cnt++] = i; else map[zero_cnt++] = i; } for (i = 0; i < one_cnt; i++) map[zero_cnt + i] = one[i];
val = map[0]; for (i = 0; i < n - 1; i++) { printf("%d ", arr[val]); val = map[val]; } printf("%d\n", arr[0]); return 0; }
题目大意: 两个人玩游戏。轮流取数字,范围在1~20之间。 规定: 1. 取过的数字不能再取。 2. 取过的数字的倍数不能再取。 3. 任意个(2)的和不能再取。
当某个人发现没有数字可取时,他就输了。 给出一个“残局”。问,我现在应该怎么取,才能保证胜利。
思路: 其实这个也不是很难的博弈问题。只是搜索就可以解决了。要用位来表示当前剩余数字的状态, 方便保存动态规划的结果。
#include <stdio.h>
#define N 20
char dp[2][1 << (N + 1)];
__inline int use(int visited, int idx) { int i;
for (i = 0; i + idx <= N; i++) { if (visited & (1 << i)) visited |= 1 << (i + idx); } return visited; }
int dfs(int my_step, int visited) { int i, r;
if (visited == (1 << (N + 1)) - 1) return !my_step; r = dp[my_step][visited]; if (r) return r - 1;
for (i = 2; i <= N; i++) { if (visited & (1 << i)) continue; r = dfs(!my_step, use(visited, i)); if (my_step && r) return 1; if (!my_step && !r) return 0; }
dp[my_step][visited] = !my_step + 1; return !my_step; }
int main() { int n, v, i, arr[24], cnt, t;
freopen("e:\\test\\in.txt", "r", stdin);
for (t = 1; scanf("%d", &n), n; t++) { v = ((1 << (N + 1)) - 1) & ~2; while (n--) { scanf("%d", &i); v &= ~(1 << i); } cnt = 0; for (i = 2; i <= N; i++) { if (v & (1 << i)) continue; if (dfs(0, use(v, i))) arr[cnt++] = i; } printf("Test Case #%d\n", t); if (cnt) { printf("The winning moves are:"); for (i = 0; i < cnt; i++) printf(" %d", arr[i]); printf("\n"); } else printf("There's no winning move.\n"); printf("\n"); }
return 0; }
#include <stdio.h> #include <string.h>
void swap(char *a, char *b) { char t = *a; *a = *b; *b = t; }
void rev(char *str) { int i, len;
len = strlen(str); for (i = 0; i < len/2; i++) swap(&str[i], &str[len - i - 1]); }
int str2int(char *str) { int i;
for (i = 0; *str; str++) i = i * 10 + *str - '0';
return i; }
int main() { int n, i, j; char a[16], b[16];
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &n); while (n--) { scanf("%s%s", a, b); rev(a); rev(b); i = str2int(a) + str2int(b); sprintf(a, "%d", i); rev(a); i = str2int(a); printf("%d\n", i); }
return 0; }
思路: 先快排,然后用公式计算出总的volume值。
#include <stdio.h>
typedef unsigned __int64 u64;
__inline void swap(u64 *a, u64 *b) { u64 t = *a; *a = *b; *b = t; }
void qs(u64 *arr, int len) { int p, r, l;
if (len < 2) return ;
l = 0; r = len - 2; p = len - 1; while (1) { while (l < p && arr[l] < arr[p]) l++; while (r >= 0 && arr[r] >= arr[p]) r--; if (l > r) break; swap(&arr[l], &arr[r]); } swap(&arr[l], &arr[p]);
qs(arr, l); qs(arr + l + 1, len - l - 1); }
u64 in[10024]; int N;
int main() { int i; u64 s;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &N); for (i = 0; i < N; i++) scanf("%llu", &in[i]); qs(in, N);
s = 0; for (i = 1; i <= N - 1; i++) s += 2 * i * (N - i) * (in[i] - in[i - 1]); printf("%llu\n", s);
return 0; }
#include <stdio.h> #include <string.h>
char map[26], rev_map[26], res[26]; int N, pre[26];
void dfs(int idx, int used) { int i;
if (idx == N) { for (i = 0; i < N; i++) printf("%c", map[res[i]] + 'a'); printf("\n"); return ; }
for (i = 0; i < N; i++) { if (used & (1 << i)) continue; if ((pre[i] & used) != pre[i]) continue; res[idx] = i; dfs(idx + 1, used | (1 << i)); } }
int main() { char a, b; int i;
freopen("e:\\test\\in.txt", "r", stdin);
while (1) {
memset(rev_map, 0, sizeof(rev_map)); while (1) { a = getchar(); if (a == EOF) return 0; if (a == '\n') break; if (a >= 'a' && a <= 'z') rev_map[a - 'a'] = 1; } for (i = N = 0; i < 26; i++) if (rev_map[i]) { map[N] = i; rev_map[i] = N++; }
memset(pre, 0, sizeof(pre)); i = 0; while (1) { a = getchar(); if (a == '\n' || a == EOF) break; if (a < 'a' || a > 'z') continue; if (i & 1) pre[rev_map[a - 'a']] |= 1 << rev_map[b - 'a']; else b = a; i++; }
dfs(0, 0); printf("\n"); }
return 0; }
摘要:
#define ICON_SIZE 32static int _HBitmapToBmp32Bits(HBITMAP hBitmap, U8 *out, int out_len){ /**//* &nb... 阅读全文
|