|
摘要: 这题很牛逼,是楼教主的《男人七题》的其中一道。求:一棵树内最短距离小于K的点对数量后来看了解题报告,原来树也是可以分治的。分:选取一条边,将一棵树分成两半,这两半的节点数要尽量相等。首先,统计个每个节点的下面有多少个节点然后,就知道每个边切断之后的情况了。选择最优的即可。治:分成两半之后,统计经过该条边的点对线段中,长度小于K的数目。Amber 大牛论文的精辟描述如下: Divide... 阅读全文
思路:
f[ch][len] = { 有多少个以 ch 开头,长度为 len 的字符串 }
#include <stdio.h>
__int64 dp[16][32], sum[16], ans;
int main() { int i, j, len; char str[16];
freopen("e:\\test\\in.txt", "r", stdin);
for (i = 0; i < 26; i++) dp[0][i] = 1; for (i = 1; i < 10; i++) { for (j = 25 - i; j >= 0; j--) { dp[i][j] += dp[i][j + 1]; dp[i][j] += dp[i - 1][j + 1]; } } for (i = 0; i < 10; i++) for (j = 0; j < 26; j++) sum[i] += dp[i][j];
scanf("%s", str); for (len = 0; str[len]; len++) ans += sum[len]; ans -= sum[len - 1]; j = 0; for (i = len - 1; i >= 0; i--) { while (j < str[len - 1 - i] - 'a') { ans += dp[i][j]; j++; } j++; } for (i = 0; i < len - 1; i++) if (str[i] >= str[i + 1]) ans = -1; printf("%I64d\n", ans + 1);
return 0; }
思路:
力矩 = 力臂*重量。这个高中物理学过啦,呵呵。 所以如果现在有一个 3 放在 -5 的位置上,一个 4 放在 3 的位置上,那当前天平的总力矩就是 3*-5 + 4*3 = -3 了
动态规划: 从小到大依次放 weight。 f[i][j] = { 已经放了 i 个 weight,天平的总力矩为 j 时候的方案个数 } 初始化 f[0][0] = 1 状态转移: 对每个位置 x f[i + 1][j + W[i + 1]*x] += f[i][j]
#include <stdio.h> #include <stdlib.h>
int C, G, X[24], W[24]; struct { int val, tm; } _dp[2][1 << 15], *dp[2] = { &_dp[0][_countof(_dp[0]) / 2], &_dp[1][_countof(_dp[1]) / 2], }, *cur, *pre; int up, down;
int main() { int i, j, k, y;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &C, &G); for (i = 1; i <= C; i++) scanf("%d", &X[i]); for (i = 1; i <= G; i++) scanf("%d", &W[i]);
dp[1][0].tm = 1; dp[1][0].val = 1; for (i = 1; i <= G; i++) { pre = dp[i & 1]; cur = dp[(i + 1) & 1]; for (j = down; j <= up; j++) { if (pre[j].tm != i) continue; for (k = 1; k <= C; k++) { y = j + W[i]*X[k]; if (cur[y].tm != i + 1) { cur[y].tm = i + 1; cur[y].val = 0; } cur[y].val += pre[j].val; if (y < down) down = y; if (y > up) up = y; } } } printf("%d\n", cur[0].val);
return 0; }
思路:
这题是道好题! 假比确定了 [2, 4]、[5, 6]、[7, 8] 的奇偶性 [4, 5] 的奇偶性无法得知 [2, 6] 的奇偶性是知道的 所以只有询问的区间符合以下要求: 1. 头部和某个已知区间的头部重合 2. 尾部和某个已知区间的尾部重合 3. 区间内每一段都是已知的 才能得出结论
我只想到这一步,然后用链表写了一遍,TLE 了。上网搜解题报告,看到“并查集”三字,恍然大悟。吗的太牛逼了。
我们把首尾相接的多个已知区间归为一类。 将这多个区间的尾部的点归在同一个集合中。 它们的父亲就是最左边区间头部。 它们的权值就是从左边区间头部的到自己的这段区间的奇偶性。
插入区间 [i, j] 的时候,首先查找 i, j 对应的父亲。就是 find 操作。 如果它们的父亲相同,则表明它们处在同一个段的多个已知区间中。 那么 i, j 的权值相减,就很容易得出 [i, j] 的奇偶性了。 如果它们的父亲不同,则需要将 i, j 分别对应的区间段关联起来。也就是 union 操作。 这时候要注意判断 i, j 父亲的位置先后。因为这个 WA 了一次。
由于节点的位置分得很散,用 hash 存放。
#include <stdio.h>
#define HASH_SIZE (1 << 18) #define NODES_NR 65536
struct node { struct node *root, *next; int idx, val; };
struct node *hash[HASH_SIZE], nodes[NODES_NR]; int nodes_cnt;
inline void find(struct node *t) { static struct node *stk[NODES_NR]; int i;
for (i = 0; t->root != t; t = t->root) stk[i++] = t; for (i--; i >= 0; i--) { stk[i]->val += stk[i]->root->val; stk[i]->root = t; } }
inline struct node *insert(int idx) { int h; struct node *t;
h = idx & (HASH_SIZE - 1); for (t = hash[h]; t; t = t->next) { if (t->idx == idx) break; } if (!t) { t = &nodes[nodes_cnt++]; t->root = t; t->idx = idx; t->next = hash[h]; hash[h] = t; } return t; }
inline int solve(int start, int end, int val) { struct node *a, *b;
a = insert(start); b = insert(end); find(a); find(b);
if (a->root == b->root) return ((b->val - a->val) & 1) == val;
val -= b->val; val += a->val; a = a->root; b = b->root; if (a->idx < b->idx) { b->root = a; b->val = val; } else { a->root = b; a->val = -val; }
return 1; }
int main() { int n, i, x, a, b; char str[16];
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &n, &x); for (i = 0; i < x; i++) { scanf("%d%d%s", &a, &b, str); if (!solve(a, b + 1, str[0] == 'o')) break; } printf("%d\n", i);
return 0; }
思路:
方程: f[i][j] = { 还剩 i 次投 dice 的机会,此时位于第 j 个格子。这种情况下赢的概率 }
状态转移: 现在投 dice。分别考虑投到 1~6 的情况,假设最后停在 k 点。 如果 k 点是 L 点。则概率 g = f[i - 2][j] 如果 k 点是 B 点。则概率 g = f[i - 1][0] 如果 k 点是一个普通的点,则概率 g = f[i - 1][j] f[i][j] = g[1]/6 + g[2]/6 + ... + g[6]/6
代码:
#include <stdio.h> #include <string.h>
double prob[3][128], *p[3]; int N, T, L, B; char map[128];
inline double calc(int x) { switch (map[x]) { case 'B': return p[1][0]; case 'L': return p[0][x]; default : return p[1][x]; } }
int main() { int i, j, c, x;
freopen("e:\\test\\in.txt", "r", stdin);
while (scanf("%d%d%d%d", &N, &T, &L, &B), N) { memset(map, ' ', sizeof(map)); memset(prob, 0, sizeof(prob)); while (L--) { scanf("%d", &i); map[i] = 'L'; } while (B--) { scanf("%d", &i); map[i] = 'B'; } //prob[1][N] = 1; for (i = 0; i < T; i++) { p[2] = prob[(i+2)%3]; p[1] = prob[(i+1)%3]; p[0] = prob[i%3]; p[1][N] = 1; for (j = 0; j < N; j++) { p[2][j] = 0; for (c = 6, x = j + 1; c && x < N; c--, x++) p[2][j] += calc(x) / 6; for ( ; c; c--, x--) p[2][j] += calc(x) / 6; } } printf("%.6lf\n", p[2][0]); }
return 0; }
思路: 这题刚开始看上去,很屌,真的。 如果用很图论的做法,就很牛逼了。 首先要把环合并为一点,然后就变成了有向无环图,然后可能用拓扑排序之类的手段解决它。 这个很难很难,反正以哥的智商是没可能想出来的。 考虑了一下,只要每头牛为起始点遍历一下图,然后统计每个点上有多少头牛能过经过就行了。 复杂度 O(NK) 还是能过的。所以就瞬间沦为一道水题了。 后来代码写出来,太爽啦 0MS,这题哥的代码是第一!
#include <stdio.h>
#define MAX_N 1024 #define MAX_E 10032
struct edge_node { struct edge_node *next; int b; };
struct vetx_node { struct edge_node *e; int cows, degs; };
struct edge_node edges[MAX_E]; struct vetx_node vetxs[MAX_N]; int K, N, M; int vis[MAX_N], tm; int queue[MAX_N], head, tail;
inline void push(int i, int d) { if (vis[i] == tm) return ; vis[i] = tm; vetxs[i].degs += d; queue[tail++] = i; }
inline void pop(int *i) { *i = queue[head++]; }
inline void bfs(int i) { int d; struct edge_node *e;
d = vetxs[i].cows; tm++; head = tail = 0; push(i, d); while (head != tail) { pop(&i); for (e = vetxs[i].e; e; e = e->next) push(e->b, d); } }
int main() { int i, a;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d", &K, &N, &M); for (i = 0; i < K; i++) { scanf("%d", &a); vetxs[a].cows++; } for (i = 0; i < M; i++) { scanf("%d%d", &a, &edges[i].b); edges[i].next = vetxs[a].e; vetxs[a].e = &edges[i]; } for (i = 1; i <= N; i++) if (vetxs[i].cows) bfs(i); a = 0; for (i = 1; i <= N; i++) if (vetxs[i].degs == K) a++; printf("%d\n", a);
return 0; }
思路: 这题数据很水,所以二维背包都能过的。
#include <stdio.h> #include <stdlib.h>
#define MAX_N 10032 #define MAX_B 1024 #define MAX_L 1024
struct node { int x, w, f, c; };
struct node in[MAX_N]; int dp[MAX_L][MAX_B], right[MAX_L]; int L, N, B;
int cmp(const void *a, const void *b) { return ((struct node *)a)->x - ((struct node *)b)->x; }
inline void update_max(int *a, int b) { if (b > *a) *a = b; }
int main() { int i, c; struct node *t;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d", &L, &N, &B); for (i = 0; i < N; i++) scanf("%d%d%d%d", &in[i].x, &in[i].w, &in[i].f, &in[i].c); qsort(in, N, sizeof(in[0]), cmp); right[0] = 1; dp[0][0] = 1; for (i = 0; i < N; i++) { t = &in[i]; for (c = 0; c < right[t->x] && c + t->c <= B; c++) { if (!dp[t->x][c]) continue; update_max(&dp[t->x + t->w][c + t->c], dp[t->x][c] + t->f); update_max(&right[t->x + t->w], c + t->c + 1); } }
for (i = c = 0; c <= B; c++) update_max(&i, dp[L][c]); printf("%d\n", i - 1);
return 0; }
思路:
由于上下都可以加空格,这个有点崩溃。 但后来发现还是可以用动态规划做的。 假设输入的字符串分别为 A,B f[i][j] = { 从 A[i] 和 B[j] 开始匹配,所能达到的最大值 } 假设 A[i] = G,B[j] = C 那么现在的情况就是 Gxxxxx Cxxxxx 状态转移为 => f[i + 1][j] + table(A[i], '-') G... -C..
=> f[i][j + 1] + table(B[j], '-') -G.. C...
=> f[i + 1][j + 1] + table(A[i], B[j]) G... C...
可以用滚动数组。
所以这样就解决了,觉得很神奇。
#include <stdio.h>
int N, M, f[2][256], *pre, *cur; char A[256], B[256], map[256]; int tbl[5][5] = { { 5, -1, -2, -1, -3}, {-1, 5, -3, -2, -4}, {-2, -3, 5, -2, -2}, {-1, -2, -2, 5, -1}, {-3, -4, -2, -1, 0}, };
inline void swap(int **a, int **b) { int *t = *a; *a = *b; *b = 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 int dif(char a, char b) { return tbl[map[a]][map[b]]; }
int main() { int t, i, j; freopen("e:\\test\\in.txt", "r", stdin);
map['A'] = 0; map['C'] = 1; map['G'] = 2; map['T'] = 3; map['-'] = 4; scanf("%d", &t); while (t--) { scanf("%d%s%d%s", &N, &A[1], &M, &B[1]); pre = &f[0][0]; cur = &f[1][0]; cur[0] = 0; for (i = 1; i <= M; i++) cur[i] = dif(B[i], '-') + cur[i - 1]; for (i = 1; i <= N; i++) { swap(&pre, &cur); cur[0] = dif(A[i], '-') + pre[0]; for (j = 1; j <= M; j++) { cur[j] = dif(A[i], B[j]) + pre[j - 1]; cur[j] = max(cur[j], dif(A[i], '-') + pre[j]); cur[j] = max(cur[j], dif(B[j], '-') + cur[j - 1]); } } printf("%d\n", cur[M]); } }
思路:
由于一共有5种物品,每种物品最多只能有5个,所以物品的拥有情况,可以表示为 6*6*6*6*6 中状态。 这个数好像是 7k 多,不大。 状态转移发生在有折扣的时候,如果当前的状态可以打折,那么就看打折之后是否会比现在划算就可以了。
一开始用的是取模,后来改成位操作了,快了一点,但还是很慢,不明白那些0ms怎么来的,⊙﹏⊙b汗 代码很烂,仅供参考
#include <stdio.h>
int S, B; int price[1024], code[1024]; int offer[128], cut[128]; int ans[1 << 15];
inline int min(int a, int b) { return a < b ? a : b; }
int main() { int c, k, i, j, n, t, m[5], a, b;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &B); t = 0; for (i = 0; i < B; i++) { scanf("%d%d%d", &c, &k, &price[i]); t |= k << (i*3); code[c] = i; } scanf("%d", &S); for (i = 0; i < S; i++) { scanf("%d", &n); offer[i] = 0; while (n--) { scanf("%d%d", &c, &k); offer[i] |= k << (code[c]*3); } scanf("%d", &cut[i]); } for (i = 0; i <= t; i++) { ans[i] = 0; a = i; for (j = 0; j < B; j++) { ans[i] += price[j] * (a & 7); a >>= 3; } for (j = 0; j < S; j++) { a = i; b = offer[j]; for (k = 0; k < B; k++) { if ((a & 7) < (b & 7)) break; a >>= 3; b >>= 3; } if (k < B) continue; ans[i] = min(ans[i], ans[i - offer[j]] + cut[j]); } } printf("%d\n", ans[t]);
return 0; }
思路:
一个 O(N^3) 的动态规划,由于 N 比较小,所以没啥问题。 f[i][j][k] = { 从一开始到三辆车分别位于 i, j, k 的时候,所有车走过的距离之和的最小值 } 其中 i <= j <= k 状态转移: 1. 第一辆车走到 k + 1 2. 第二辆车走到 k + 1 3. 第三辆车走到 k + 1
注意: 两点之间的距离跟输入一致。 不可以计算两点间的最短距离,这样会 WA。 这是题目没有描述清楚!
#include <stdio.h>
#define MAX_N 32 #define MAX_DIS 0x70000000
int M, N; int D[MAX_N][MAX_N]; int dp[MAX_N][MAX_N][MAX_N];
inline void update(int *a, int b) { if (b < *a) *a = b; }
int main() { int i, j, k, v;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &M); while (M--) { scanf("%d", &N); for (i = 1; i <= N - 1; i++) { for (j = i + 1; j <= N; j++) { scanf("%d", &v); D[i][j] = D[j][i] = v; } } /**//* 两点之间的距离跟输入一致。 不可以计算两点间的最短距离,这样会 WA。 这是题目没有描述清楚! for (k = 1; k <= N; k++) for (i = 1; i <= N; i++) for (j = 1; j <= N; j++) if (D[i][k] + D[k][j] < D[i][j]) D[i][j] = D[i][k] + D[k][j]; */ for (i = 1; i <= N; i++) for (j = 1; j <= N; j++) for (k = 1; k <= N; k++) dp[i][j][k] = MAX_DIS; dp[1][1][1] = 0; for (i = 1; i <= N - 1; i++) { for (j = 1; j <= i; j++) { for (k = j; k <= i; k++) { update(&dp[k][i][i + 1], dp[j][k][i] + D[j][i + 1]); update(&dp[j][i][i + 1], dp[j][k][i] + D[k][i + 1]); update(&dp[j][k][i + 1], dp[j][k][i] + D[i][i + 1]); } } } v = MAX_DIS; for (i = 1; i <= N; i++) for (j = i; j <= N; j++) update(&v, dp[i][j][N]); printf("%d\n", v); }
return 0; }
|