|
题目大意: 给出一个序列,长度为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... 阅读全文
|