|
这题不懂做,一开始想了一个动态规划的方法,复杂度很高。估计得几百ms。 看status,觉得肯定有很低复杂度的方法。 然后看了 Discuss ,看到某位大牛说的贪心方法,顿时恍然大悟,吗的实在太牛逼了。 这位大牛的解法如下: 想象自己是一个黑一日游的司机: 1.如果有乘客要上车,那么就让他上,收钱! 2.如果超载了,把距目的地最远的几个乘客踢下去,退钱。 3.行驶到下一站
照着这个方法编码,一开始速度很慢,后来改成用一个队列来维护车上的乘客,速度就很快了,还飚上榜了。
#include <stdio.h> #include <stdlib.h>
#define MAX_N 10032 #define MAX_K 50032
struct slot_node { int end, cap; struct slot_node *next; }; struct stat_node { int idx[MAX_N], cnt[MAX_N], head, tail, tot; }; struct slot_node *start[2][MAX_N], slots[MAX_K*2]; struct stat_node stat[2]; int K, N, C, left, right, slots_cnt, ans;
inline int min(int a, int b) { return a < b ? a : b; }
inline void ins(int a, int b, int c, int d) { struct slot_node *t;
if (b > right) right = b; if (a < left) left = a; t = &slots[slots_cnt++]; t->next = start[d][a]; t->end = b; t->cap = c; start[d][a] = t; }
inline void off(int d, int i) { struct stat_node *t = &stat[d];
if (t->head == t->tail || t->idx[t->head] != i) return ; ans += t->cnt[t->head]; t->tot -= t->cnt[t->head]; t->head++; }
inline void del(struct stat_node *t) { int c;
while (t->tot > C) { c = min(t->tot - C, t->cnt[t->tail - 1]); t->cnt[t->tail - 1] -= c; t->tot -= c; if (!t->cnt[t->tail - 1]) t->tail--; } }
inline void add(struct stat_node *t, int cap, int end) { int i, j;
t->tot += cap;
for (i = t->tail - 1; i >= t->head; i--) { if (t->idx[i] == end) { t->cnt[i] += cap; return ; } else if (t->idx[i] < end) break; } i++; t->tail++; for (j = t->tail - 1; j > i; j--) { t->idx[j] = t->idx[j - 1]; t->cnt[j] = t->cnt[j - 1]; } t->idx[i] = end; t->cnt[i] = cap; }
inline void on(int d, int i) { struct slot_node *s; struct stat_node *t = &stat[d];
for (s = start[d][i]; s; s = s->next) add(t, s->cap, s->end); del(t); }
int main() { int i, a, b, c;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d", &K, &N, &C); left = N; for (i = 0; i < K; i++) { scanf("%d%d%d", &a, &b, &c); if (a > b) ins(N - a, N - b, c, 1); else ins(a, b, c, 0); }
for (i = left; i <= right; i++) { off(0, i); off(1, i); on(0, i); on(1, i); } printf("%d\n", ans);
return 0; }
思路:
f[a][b] = { 种类数目为 a,蚂蚁数目为 b 时候的方案总数 } 转移: f[a][b] = f[a - 1][0] + f[a - 1][1] + ... + f[a - 1][b]
时间 O(AT) 如果求 f[a][*] 只用一次循环的话 可以用循环数组
杯具: 把i看成j了,足足调了3个小时,注意,是不吃不喝,也没有上厕所,没有听歌,没有看优酷。。 是精神高度集中地浪费了3个小时! 与非主流之脑残相比,有过之而无不及也。
#include <stdio.h>
#define P 1000000
int T, A, S, B, fam[1024], dp[2][1024*128], *cur, *pre;
inline int min(int a, int b) { return a < b ? a : b; }
int main() { int i, j, cnt, end, sum;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d%d", &T, &A, &S, &B); for (i = 0; i < A; i++) { scanf("%d", &j); fam[j]++; } for (i = 0; i <= fam[1]; i++) dp[1][i] = 1; end = fam[1];
for (i = 2; i <= T; i++) { cur = dp[i & 1]; pre = dp[(i+1) & 1]; cur[0] = pre[0]; end += fam[i]; for (j = 1; j <= end; j++) { cur[j] = cur[j - 1] + pre[j]; if (j > fam[i]) cur[j] -= pre[j - fam[i] - 1]; cur[j] += P; cur[j] %= P; } }
sum = 0; for (i = S; i <= B; i++) { sum += cur[i]; sum %= P; }
printf("%d\n", sum);
return 0; }
首先考虑这个子问题: 从地图上的某一点开始一直向右上方发展,一共能组成多少个以这个点的字母为开头的单词。 那么要把以这个点为左下角的矩形中的点全部都考察一遍。 假设这个点的字母为'N',那就要看所有单词组成的单词树里面,所有'N'节点孩子的字母,是否出现在这个矩形中。 如果有出现的话,则又是一个子问题了。 把所有这样的子问题的和求出来,就是答案了。
再考虑一个细节: 'N'节点在单词树中可以出现很多次,因此每个'N'节点都是一个子问题。表示: 所有组成的单词,第一个字母节点是'N',余下的字母节点出现的'N'的子树里。 动态规划的时候,对于每个节点要处理一次,因为每个节点的孩子都不一样。 在矩形中统计所有孩子对应的子问题的和。
关键是怎么存放子问题的答案: 我们需要找出矩形中跟指定孩子对应的点。如果把答案存放在地图W*H的数组里面,那就比较麻烦,需要枚举。 但是如果存放在单词树里面,就好很多。 如果我们从上往下扫描每一行,每一行从右到左扫描。那就只需要存放长度为W的一维数组了。
考虑一个细节: 对于地图上的点'N',要以什么样的顺序处理单词树的每个'N'。 按照单词树的位置,应该从上到下处理。
代码写出来,跑了100ms+,居然排到第9,然后换了gcc编译,又排前了一点! 发现前面的人,除了第二名,估计算法都是一样的。
#include <stdio.h>
#define SIZE 64 #define QUEUE_SIZE 4096
struct tree_node { int end, dp[SIZE]; struct tree_node *child[26], *next; };
struct queue_node { int idx; struct tree_node *ptr; };
int H, W; char map[SIZE][SIZE]; struct tree_node nodes[QUEUE_SIZE], root, *hash[26]; int nodes_cnt; struct queue_node queue[QUEUE_SIZE]; int tail, head;
inline void bfs() { struct tree_node *t; int i;
head = tail = 0; queue[tail++].ptr = &root; while (head != tail) { t = queue[head++].ptr; for (i = 0; i < 26; i++) { if (!t->child[i]) continue; queue[tail].ptr = t->child[i]; queue[tail].idx = i; tail++; } } for (tail--; tail >= 1; tail--) { t = queue[tail].ptr; i = queue[tail].idx; t->next = hash[i]; hash[i] = t; } }
inline void calc(int y, int x) { struct tree_node *t, *c; int i, j, sum;
for (t = hash[map[y][x] - 'A']; t; t = t->next) { sum = t->end; for (i = 0; i < 26; i++) { c = t->child[i]; if (!c) continue; for (j = W - 1; j >= x; j--) sum += c->dp[j]; } t->dp[x] += sum; } }
int main() { int i, j, sum; char str[64], *s; struct tree_node *t, **p;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &H, &W); for (i = 0; i < H; i++) scanf("%s", map[i]); while (scanf("%s", str) != EOF) { t = &root; for (s = str; *s; s++) { p = &t->child[*s - 'A']; if (!*p) *p = &nodes[nodes_cnt++]; t = *p; } t->end++; } bfs();
for (i = 0; i < H; i++) for (j = W - 1; j >= 0; j--) calc(i, j);
sum = 0; for (i = 0; i < 26; i++) { t = root.child[i]; if (!t) continue; for (j = 0; j < W; j++) sum += t->dp[j]; } printf("%d\n", sum);
return 0; }
思路: 就是一个最短路径的问题。但是题目数据规模跟描述不符合。 数组要开大一些才能过。官方数据是描述是符合的,可能是管理员加了一些进去。
#include <stdio.h> #include <string.h> #include <stdlib.h>
#define MAX_E 4096 #define MAX_V 2048 #define MAX_COST (1 << 30)
struct edge_node { int idx, cost; struct edge_node *next; };
struct graph_node { struct edge_node edges[MAX_E], *map[MAX_V]; int edges_cnt, vertexs_cnt; int min_dis[MAX_V]; };
inline int min(int a, int b) { return a < b ? a : b; }
inline void graph_init(struct graph_node *g, int vertexs_cnt) { g->vertexs_cnt = vertexs_cnt; g->edges_cnt = 0; memset(g->map, 0, vertexs_cnt * sizeof(g->map[0])); }
inline void graph_addedge(struct graph_node *g, int from, int to, int cost ) { struct edge_node *e;
e = &g->edges[g->edges_cnt++]; e->idx = to; e->cost = cost; e->next = g->map[from]; g->map[from] = e; }
inline void graph_spfa(struct graph_node *g, int idx) { static int queue[MAX_V], vis[MAX_V], tm, head, tail; int i, val; struct edge_node *e;
for (i = 0; i < g->vertexs_cnt; i++) g->min_dis[i] = MAX_COST; g->min_dis[idx] = 0; head = tail = 0; tm++; queue[tail++] = idx;
while (head != tail) { idx = queue[head++]; vis[idx] = 0; for (e = g->map[idx]; e; e = e->next) { val = g->min_dis[idx] + e->cost; if (val >= g->min_dis[e->idx]) continue; g->min_dis[e->idx] = val; if (vis[e->idx] == tm) continue; queue[tail++] = e->idx; vis[e->idx] = tm; } } }
int main() { static int loc[MAX_V], i, F, C, P, M, from, to, cost, cnt; static struct graph_node g;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d%d", &F, &P, &C, &M); graph_init(&g, F + 1); while (P--) { scanf("%d%d%d", &from, &to, &cost); graph_addedge(&g, from, to, cost); graph_addedge(&g, to, from, cost); } for (i = 0; i < C; i++) scanf("%d", &loc[i]);
graph_spfa(&g, 1); cnt = 0; for (i = 0; i < C; i++) cnt += (g.min_dis[loc[i]] <= M); printf("%d\n", cnt); for (i = 0; i < C; i++) if (g.min_dis[loc[i]] <= M) printf("%d\n", i + 1);
return 0; }
摘要: 第一次写这玩意,还真有点麻烦,出了点问题。然后看了一下别人的代码,发现双向边,要分成两个单向边来插入。长见识了。而且费用的值有正有负,最好用SPFA来找最短路径。思路:建立一个超级源点,跟点1连起来,容量为2。建立一个超级汇点,跟点N连起来,容量为2。其他的边容量均为1。#include <stdio.h>#include <stdlib.h>#inc... 阅读全文
#include <stdio.h>
int L, C; char path[26], arr[26], map[256];
void dfs(int i, int vowels, int idx) { if (idx == L) { if (vowels) printf("%s\n", path); return ; } for ( ; i <= C - L + idx; i++) { path[idx] = arr[i]; dfs(i + 1, vowels + (arr[i] == 'a' || arr[i] == 'e' || arr[i] == 'i' || arr[i] == 'o' || arr[i] == 'u'), idx + 1); } }
int main() { int i, j; char str[8];
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &L, &C); for (i = 0; i < C; i++) { scanf("%s", str); map[str[0]]++; } j = 0; for (i = 'a'; i <= 'z'; i++) if (map[i]) arr[j++] = i; path[L] = 0; dfs(0, 0, 0);
return 0; }
思路:
留意到题目里面有一句话“All farms are connected one way or another to Farm 1.”。 这貌似说明图一开始就是连通的。
二分答案,判断依据是: 如果将大于某个长度的边都去掉以后,图就不连通了。 那这个长度相对答案来说,一定是大了。
#include <stdio.h>
#define MAX_M 10032 #define MAX_N 2048
struct edge_node { int idx, len; struct edge_node *next; };
struct edge_node edges[MAX_M*2], *map[MAX_N]; int N, M, vis[MAX_N], tm, stk[MAX_N], *sp;
inline int can(int limit) { int cnt; struct edge_node *e;
tm++; sp = stk; *sp++ = 1; vis[1] = tm; cnt = 0; while (sp > stk) { sp--; cnt++; for (e = map[*sp]; e; e = e->next) { if (vis[e->idx] == tm || e->len > limit) continue; vis[e->idx] = tm; *sp++ = e->idx; } }
return cnt == N; }
inline void insert(struct edge_node *e, int a, int b, int len) { e->idx = b; e->len = len; e->next = map[a]; map[a] = e; }
int main() { int l, r, m, i, a, b, len;
scanf("%d%d", &N, &M); l = 0x7fffffff; r = 0; for (i = 0; i < M*2; i += 2) { scanf("%d%d%d", &a, &b, &len); insert(&edges[i], a, b, len); insert(&edges[i + 1], b, a, len); if (len < l) l = len; if (len > r) r = len; }
while (l <= r) { m = (l + r) / 2; if (!can(m)) l = m + 1; else r = m - 1; } printf("%d\n", r + 1);
return 0; }
思路:
每天要么以今天的价格买入,要么前面某天买了存在仓库里。不存在一半是今天买的,一半是从仓库拿的,这种情况。 因为如果这样,肯定有一半具有更高的性价比,那就全部都改成更高性价比的方式就好了。。 所以,这题只需要保存一个当前的最大值,扫描一遍就能出结果了。瞬间就沦为一道水题了。还以为要大动作的呢。
#include <stdio.h>
__int64 N, S;
__inline __int64 min(__int64 a, __int64 b) { return a < b ? a : b; }
int main() { __int64 best, i, c, y, sum, inc;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%I64d%I64d", &N, &S); best = 1LL << 60; sum = 0; inc = (N - 1) * S; for (i = 0; i < N; i++) { scanf("%I64d%I64d", &c, &y); best = min(best, c + inc); sum += y * (best - inc); inc -= S; } printf("%I64d\n", sum);
return 0; }
思路: 深搜的时候,可以生成一棵树。 深搜也就是深度遍历这棵树,把遍历的路径打印出来,就解决了一部分边了,这部分边都是经过两次的,来一次去一次。 剩下的边,就是遍历的时候正在访问的节点与已经访问的节点之间的边,很容易的判断的。同样把这部分路径也打印出来。 后来看了 Discuss 才发现,这个东西叫做“欧拉回路”,又长见识了。 代码
#include <stdio.h>
#define MAX_M 50032 #define MAX_N 10032
int N, M;
struct edge_node { int vis, to; struct edge_node *next; }; struct edge_node edges[MAX_M * 2], *map[MAX_N]; int edges_cnt, vis[MAX_N];
inline void insert(struct edge_node *e, int from, int to) { e->to = to; e->next = map[from]; map[from] = e; }
void dfs(int idx) { struct edge_node *e; int i;
vis[idx] = 1; printf("%d\n", idx);
for (e = map[idx]; e; e = e->next) { i = e - edges; if (vis[e->to]) { if (e->vis) continue; edges[i].vis = 1; edges[i ^ 1].vis = 1; printf("%d\n%d\n", e->to, idx); continue; } edges[i].vis = 1; edges[i ^ 1].vis = 1; dfs(e->to); printf("%d\n", idx); } }
int main() { int from, to, i;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &N, &M); for (i = 0; i < M*2; i += 2) { scanf("%d%d", &from, &to); insert(&edges[i], from, to); insert(&edges[i + 1], to, from); } dfs(1);
return 0; }
#include <stdio.h> #include <string.h>
int T, N, arr[16], map[16][16];
int dfs(int idx, int i) { if (idx >= N) return 1; if (!arr[idx]) return dfs(idx + 1, idx + 2); for ( ; i < N; i++) { if (!arr[i]) continue; arr[i]--; arr[idx]--; map[idx][i]++; map[i][idx]++; if (dfs(idx, i + 1)) return 1; arr[i]++; arr[idx]++; map[idx][i]--; map[i][idx]--; } return 0; }
int main() { int i, j;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &T); while (T--) { scanf("%d", &N); for (i = 0; i < N; i++) scanf("%d", &arr[i]); memset(map, 0, sizeof(map)); if (dfs(0, 1)) { printf("YES\n"); for (i = 0; i < N; i++) { for (j = 0; j < N; j++) printf("%d ", map[i][j]); printf("\n"); } printf("\n"); } else printf("NO\n\n"); }
return 0; }
|