|
朴素的Prim!
#include <stdio.h>

int map[128][128], set[128], N, tm;

__inline int prim()
  {
int sum, i, j, k, min_val, min_idx;

tm++;
set[0] = tm;
sum = 0;
 for (i = 0; i < N - 1; i++) {
min_val = 0x7fffffff;
 for (j = 0; j < N; j++) {
if (set[j] != tm)
continue;
 for (k = 0; k < N; k++) {
 if (set[k] != tm && map[j][k] < min_val) {
min_val = map[j][k];
min_idx = k;
}
}
}
sum += min_val;
set[min_idx] = tm;
}

return sum;
}

int main()
  {
int i, j;

freopen("e:\\test\\in.txt", "r", stdin);

 while (scanf("%d", &N) != EOF) {
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
scanf("%d", &map[i][j]);
printf("%d\n", prim());
}

return 0;
}
用的SPFA算法。
#include <stdio.h>
#include <stdlib.h>

#define MAX_N 1024
#define MAX_T 2048

 struct edge_node {
int idx, len;
struct edge_node *next;
};
struct edge_node edges[MAX_T * 2], *map[MAX_N];
int N, T, D[MAX_N], queue[MAX_N], qh, qt, visited[MAX_N];

__inline void add_edge(struct edge_node *e, int from, int to, int len)
  {
e->idx = to;
e->len = len;
e->next = map[from];
map[from] = e;
}

int main()
  {
int i, from, to, len;
struct edge_node *e;

freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &T, &N);
 for (i = 0; i < T * 2; i += 2) {
scanf("%d%d%d", &from, &to, &len);
add_edge(&edges[i], from, to, len);
add_edge(&edges[i + 1], to, from, len);
}
for (i = 1; i <= N; i++)
D[i] = 0x00ffffff;

queue[0] = N;
visited[N] = 1;
D[N] = 0;
qh = 0;
qt = 1;
 while (qh != qt) {
i = queue[qh++];
qh &= _countof(queue) - 1;
visited[i] = 0;
 for (e = map[i]; e; e = e->next) {
 if (D[i] + e->len < D[e->idx]) {
D[e->idx] = D[i] + e->len;
 if (!visited[e->idx]) {
visited[e->idx] = 1;
queue[qt++] = e->idx;
qt &= _countof(queue) - 1;
}
}
}
}
printf("%d\n", D[1]);

return 0;
}

注意: 要去掉前面的0再输出。
#include <stdio.h>
#include <string.h>

 struct node {
char arr[128];
int len;
};

__inline int max(int a, int b)
  {
return a > b ? a : b;
}

__inline void add(struct node *a, struct node *b)
  {
int i, val;

 for (val = i = 0; i < max(a->len, b->len); i++) {
if (i < a->len)
val += a->arr[i];
if (i < b->len)
val += b->arr[i];
a->arr[i] = val % 10;
val /= 10;
}
if (val)
a->arr[i++] = val;
a->len = i;
}

__inline void mul_single(struct node *a, int val, int pad, struct node *c)
  {
int i, r;

for (i = 0; i < pad; i++)
c->arr[i] = 0;
c->len = pad;
 for (r = i = 0; i < a->len; i++) {
r += a->arr[i] * val;
c->arr[c->len++] = r % 10;
r /= 10;
}
if (r)
c->arr[c->len++] = r;
}

__inline void mul(struct node *a, struct node *b, struct node *c)
  {
struct node t;
int i;

c->len = 0;
 for (i = 0; i < a->len; i++) {
mul_single(b, a->arr[i], i, &t);
add(c, &t);
}
}

__inline void input(struct node *t)
  {
char str[sizeof(t->arr)];
int i, len;

scanf("%s", str);
len = strlen(str);
t->len = 0;
for (i = len - 1; i >= 0; i--)
t->arr[t->len++] = str[i] - '0';
}

__inline void output(struct node *t)
  {
int i;

for (i = t->len - 1; i >= 0 && !t->arr[i]; i--);
 if (i < 0) {
printf("0\n");
return ;
}
for ( ; i >= 0; i--)
putc(t->arr[i] + '0', stdout);
putc('\n', stdout);
}

int main()
  {
struct node a, b, c;

freopen("e:\\test\\in.txt", "r", stdin);

input(&a);
input(&b);
mul(&a, &b, &c);
output(&c);

return 0;
}

纪念一下,跟我生日一样的题目。 思路: 这题可以用并查集来做,也是挺取巧的。 每个栈看做是一个集合,用一个数组记录栈中元素离栈底的距离,一个数组记录每个栈底元素对应的栈顶的元素。 对于移动操作,只需要合并集合,然后更改栈顶元素数组就行了。 用了栈写的路径压缩,代码跑到230ms。不知道那些100ms是怎么搞出来的。。真的有什么神奇技巧吗。
#include <stdio.h>

#define MAX_N 30032

int top[MAX_N];
 struct set_node {
int parent, dis;
};
struct set_node set[MAX_N];

__inline int find(int idx)
  {
static int stk[MAX_N], sp, i;

 for (sp = 0; set[idx].parent; sp++) {
stk[sp] = idx;
idx = set[idx].parent;
}
 for (sp--; sp >= 0; sp--) {
i = stk[sp];
set[i].dis += set[set[i].parent].dis;
set[i].parent = idx;
}

return idx;
}

int main()
  {
int p, a, b;
char op[16];

freopen("e:\\test\\in.txt", "r", stdin);

for (a = 0; a < MAX_N; a++)
top[a] = a;

scanf("%d", &p);
 while (p--) {
scanf("%s%d", op, &a);
 if (op[0] == 'M') {
scanf("%d", &b);
a = find(a);
b = find(b);
set[a].parent = top[b];
set[a].dis = 1;
top[b] = top[a];
 } else {
find(a);
printf("%d\n", set[a].dis);
}
}

return 0;
}

思路: 由于W的值 <= 30,比较小,所以这题可以用动态规划来做。 首先要把连续同一个数字一次处理。 dp[i] = {走了 i 次以后,得到的最大的苹果数目}。这个数组的大小为 W。 走了奇数次以后,一定位于树2下面。 走了偶数次以后,一定位于树1下面。 假设当前是在第 t 时刻掉了 cnt 个苹果下来。val 表示哪棵树掉的苹果,则执行下面的操作更新数组就可以了。
 if (val == 1) {
for (i = 0; i <= min(t, W); i += 2)
dp[i] += cnt;
for (i = 1; i <= min(t, W); i += 2)
dp[i + 1] = max(dp[i + 1], dp[i] + cnt);
 } else {
for (i = 1; i <= min(t, W); i += 2)
dp[i] += cnt;
for (i = 0; i <= min(t, W); i += 2)
dp[i + 1] = max(dp[i + 1], dp[i] + cnt);
}
转移方程就是这个,还是挺简单的。
因为数据弱,代码 0ms ac了。
完整代码:
#include <stdio.h>

int T, W, dp[35], t;

__inline int max(int a, int b)
  {
return a > b ? a : b;
}

__inline int min(int a, int b)
  {
return a < b ? a : b;
}

__inline void calc(int val, int cnt)
  {
int i;

 if (val == 1) {
for (i = 0; i <= min(t, W); i += 2)
dp[i] += cnt;
for (i = 1; i <= min(t, W); i += 2)
dp[i + 1] = max(dp[i + 1], dp[i] + cnt);
 } else {
for (i = 1; i <= min(t, W); i += 2)
dp[i] += cnt;
for (i = 0; i <= min(t, W); i += 2)
dp[i + 1] = max(dp[i + 1], dp[i] + cnt);
}
t++;
}

int main()
  {
int i, pre, cnt;

freopen("e:\\test\\in.txt", "r", stdin);

scanf("%d%d%d", &T, &W, &pre);
cnt = 1;
 while (--T) {
scanf("%d", &i);
 if (i == pre) {
cnt++;
continue;
}
calc(pre, cnt);
cnt = 1;
pre = i;
}
calc(pre, cnt);

cnt = 0;
for (i = 0; i <= W; i++)
cnt = max(cnt, dp[i]);
printf("%d\n", cnt);

return 0;
}

昨天做了2008,今天准备做2009。但是看了下题目,发现爆难,才100个人过。 觉得这种题还是别碰了,等以后牛逼了再做。 于是跳过2008年,直接到2010年了!呵呵。
这题还是算容易的,比较适合自己水平发挥,用堆来做,速度尚可 188ms 。
思路: 先把牛按照score排序一下,然后从后往前找,把每一头牛当做是位于中间的那头牛。 那现在就是求: 该头牛前面的所有牛中,哪 (N - 1) / 2 头牛aid值的和最小。 该头牛后面的所有牛中,哪 (N - 1) / 2 头牛aid值的和最小。 这就是典型的用堆可以解决的问题了。
#include <stdio.h>
#include <stdlib.h>

#define MAX_C 100032
#define MAX_N 20032

 struct node {
int score, aid;
};
struct node in[MAX_C];
int N, C, F;
int after[MAX_C], before[MAX_C];
int heap_size, heap_sum, heap[MAX_N];

int cmp(const void *a, const void *b)
  {
return ((struct node *)a)->score - ((struct node *)b)->score;
}

__inline void shift_down(int idx)
  {
int val = heap[idx];
 while (1) {
idx *= 2;
if (idx > heap_size)
break ;
if (idx + 1 <= heap_size && heap[idx + 1] > heap[idx])
idx++;
if (heap[idx] <= val)
break;
heap[idx / 2] = heap[idx];
}
heap[idx / 2] = val;
}

__inline int heap_init(int start, int len)
  {
int i;

heap_sum = 0;
 for (i = start; i < start + len; i++) {
heap[i - start + 1] = in[i].aid;
heap_sum += in[i].aid;
}
for (i = heap_size / 2; i >= 1; i--)
shift_down(i);
return heap_sum;
}

__inline int heap_update(int aid)
  {
 if (aid < heap[1]) {
heap_sum -= heap[1] - aid;
heap[1] = aid;
shift_down(1);
}
return heap_sum;
}

int main()
  {
int i;

freopen("e:\\test\\in.txt", "r", stdin);

scanf("%d%d%d", &N, &C, &F);
for (i = 0; i < C; i++)
scanf("%d%d", &in[i].score, &in[i].aid);
qsort(in, C, sizeof(in[0]), cmp);
heap_size = (N - 1) / 2;
before[heap_size - 1] = heap_init(0, heap_size);
for (i = heap_size; i < C; i++)
before[i] = heap_update(in[i].aid);
after[C - heap_size] = heap_init(C - heap_size, heap_size);
for (i = C - heap_size - 1; i >= 0; i--)
after[i] = heap_update(in[i].aid);
 for (i = C - heap_size - 1; i - heap_size >= 0; i--) {
if (in[i].aid + before[i - 1] + after[i + 1] <= F)
break;
}
printf("%d\n", i - heap_size < 0 ? -1 : in[i].score);

return 0;
}

思路: 这道题目的解法非常牛逼。刚一看题就知道做不出来了,所以就在这个博客 http://hi.baidu.com/findthegateopen/ 找到了一份解题报告。下面的内容都是基于原作者的代码参考出来的。感谢原作者的代码!
朴素的做法是O(N^3)的复杂度。usaco官方的算法是O(N^2)的复杂度。原作者的代码跑了不到100ms,应该说是相当不错了!
首先,要把所有牛放到坐标系上来表示。目的,就是求出包含最多点的直角三角形。 直角三角形的两条直角边上都必须有点,也就是一组牛中的具有最小height的点和具有最小width的点。 直角三角形的边长也是固定的,cw = C/B,ch = C/A。这个还好说,从那个限制条件可以推出来的。初中都学过,呵呵。

Step1:求出经过一个点的所有可能存在的三角形。 其实也就是在该点下方的灰色区域中选择点来确定一个三角形。
  Step2:求出经过一个点的所有可能存在的三角形中,最多包含的点数。 解法相当精妙。 求一个三角形内的点数,可以分解为一个矩形内的点数减去一个梯形内的点数。  用这个方法,求出最上面那个三角形的点数之后。可以继续递推得到下面其他三角形的点数。  也就是加上一个矩形,再减去一个梯形。 如果点按照高度排序以后,那么后面矩形里的点一定是后出现的。这样就可以做到随时增加矩形。 但是减去梯形这个操作,就难理解一点,把点按照A*H + B*W来排序,就能保证后面梯形里的点一定是后出现的。  可见,A*H + B*W 值的大小决定了他们的位置分布。完全可以保证这个顺序。 这种数形结合的方法实在是相当精妙! 那我们就可以首先求出第一个三角形的点数,然后接下来的三角形就根据减去梯形,和增加矩形的操作,来做小的调整就可以了。 在代码里面的表现形式就是维护两个指针,不断向后移,中间剔除横坐标不在范围之内的点。 这个操作的复杂度是O(N)。 对所有点执行一次,故算法的复杂度是O(N^2)。 代码:
 /**//*
* 本代码参考自 http://hi.baidu.com/findthegateopen/
* 中的代码,感谢原作者的代码!
*/
#include <stdio.h>
#include <stdlib.h>

#define MAX_N 1024

 struct node {
int w, h, k;
};

struct node in[MAX_N], *sort_h[MAX_N], *sort_k[MAX_N];
int A, B, C, N, ch, cw, ans, box, slash, cnt;

int cmp_h(const void *a, const void *b)
  {
return (*(struct node **)b)->h - (*(struct node **)a)->h;
}

int cmp_k(const void *a, const void *b)
  {
return (*(struct node **)b)->k - (*(struct node **)a)->k;
}

__inline void update(int h, int w)
  {
int k;

for ( ; box < N && sort_h[box]->h >= h; box++)
if (sort_h[box]->w >= w && sort_h[box]->w <= w + cw)
cnt++;
k = A * h + B * w + C;
for ( ; slash < N && sort_k[slash]->k > k; slash++)
if (sort_k[slash]->w >= w && sort_k[slash]->w <= w + cw)
cnt--;
if (cnt > ans)
ans = cnt;
}

__inline void calc(int i)
  {
int h, w;

box = 0;
slash = 0;
cnt = 0;
h = sort_h[i]->h;
w = sort_h[i]->w;
for ( ; i < N && sort_h[i]->h >= h - ch; i++)
if (sort_h[i]->w >= w && sort_h[i]->w <= w + cw)
update(sort_h[i]->h, w);
}

int main()
  {
int i;

freopen("e:\\test\\in.txt", "r", stdin);

scanf("%d%d%d%d", &N, &A, &B, &C);
cw = C/B;
ch = C/A;
 for (i = 0; i < N; i++) {
scanf("%d%d", &in[i].h, &in[i].w);
in[i].k = A * in[i].h + B * in[i].w;
sort_h[i] = &in[i];
sort_k[i] = &in[i];
}
qsort(sort_h, N, sizeof(sort_h[0]), cmp_h);
qsort(sort_k, N, sizeof(sort_k[0]), cmp_k);

for (i = 0; i < N; i++)
calc(i);
printf("%d\n", ans);

return 0;
}


思路: 1985也可以用1986的程序改改就行了。 但是觉得不用什么算法也是可以做出1985的。 想了一下,发现: 路径的最大值一定存在于两个叶子节点中。 如果只有一个叶子,那整个树就是一条直线了。 由于我们只是考虑叶子节点。那么对于每一个非叶子节点,我们只需要找出它下面的所有节点中,离它最远的两个叶子就行了。 这两个叶子节点的距离也就有可能成为答案。 对于每个点,我们只需要保存一个值,就是该点下面的所有节点中,距离它最远的一个叶子节点,和它的距离。 对于每个点,遍历完它的孩子之后,就知道“离它最远的两个叶子的距离”了。 注意: 代码里需要处理“一条直线连着几个点”这种情况,将这样的几个点缩成一个点比较好。不做这个处理一定会爆栈。最后一个数据是一条直线。(阴险) 这份代码跑了141MS,还算可以,呵呵。应该比直接用lca要快。
#include <stdio.h>

#define MAX_N 40032

 struct edge_node {
struct edge_node *next;
int idx, len;
};
struct edge_node edges[MAX_N*2];

 struct tree_node {
struct edge_node *edge;
int visited;
};
struct tree_node tree[MAX_N];
int max_val;

__inline void add_edge(int idx, int a, int b, int len)
  {
struct edge_node *e = &edges[idx];
e->idx = b;
e->len = len;
e->next = tree[a].edge;
tree[a].edge = e;
}

int dfs(int idx)
  {
struct edge_node *e;
int sum, cnt, arr[2], r;

sum = 0;
 while (1) {
tree[idx].visited = 1;
cnt = 0;
for (e = tree[idx].edge; e; e = e->next)
cnt += !tree[e->idx].visited;
if (!cnt)
return sum;
if (cnt > 1)
break;
for (e = tree[idx].edge; tree[e->idx].visited; e = e->next);
sum += e->len;
idx = e->idx;
}

arr[0] = arr[1] = 0;
 for (e = tree[idx].edge; e; e = e->next) {
if (tree[e->idx].visited)
continue;
r = dfs(e->idx) + e->len;
 if (r >= arr[1]) {
arr[0] = arr[1];
arr[1] = r;
} else if (r >= arr[0])
arr[0] = r;
}

r = arr[0] + arr[1];
if (r > max_val)
max_val = r;

return arr[1] + sum;
}

int main()
  {
int m, n, a, b, len, i;
char str[16];

freopen("e:\\test\\in.txt", "r", stdin);

scanf("%d%d", &n, &m);
 for (i = 0; i < m*2; i += 2) {
scanf("%d%d%d%s", &a, &b, &len, str);
add_edge(i, a, b, len);
add_edge(i + 1, b, a, len);
}

 for (i = 1; i <= n; i++) {
if (tree[i].visited)
continue;
a = dfs(i);
if (a > max_val)
max_val = a;
}
printf("%d\n", max_val);

return 0;
}

摘要: 以前没见过“最近公共祖先”这一类的题啊。长见识了,呵呵。解法基本上就是http://www.cppblog.com/varg-vikernes/archive/2010/03/10/109355.html这篇文章里面提到的两种方法。两种方法的解法都非常精妙!最后程序写出来:Tarjan 3372K 219MSDFS+RMQ 7992K 329MS代码Tarjan:... 阅读全文
|