|
思路:
它的要求是,给你几个字母,用这些字母拼几个字典里面有的单词,所有单词加起来求最高得分。 转化一下,就是个01背包问题。 由于单词的长度很短很短了,只有3~7个字母,所以总状态数很少啦。数组开到 2048 就可以AC了。
后来又搜了一下别人的解题报告哦,发现有个哥们很牛逼。 他说:单词长度范围在3--7内。所以可能的词组 只能是 3+3 或 3+4
一语惊醒脑残人。太牛逼了! 代码 150ms AC。
#include <stdio.h>
char key[] = { "qwertyuiop" "asdfghjkl" "zxcvbnm" }; int score[] = { 7, 6, 1, 2, 2, 5, 4, 1, 3, 5, 2, 1, 4, 6, 5, 5, 7, 6, 3, 7, 7, 4, 6, 5, 2, 5 };
int map[256], col[256], idx[256], mul[8], tot[8], cnt, hash[2048], top;
int can_add(int a, int b) { int i, ia, ib;
for (i = 1; i < cnt; i++) { ia = (a / mul[i - 1]) % tot[i]; ib = (b / mul[i - 1]) % tot[i]; if (ia + ib >= tot[i]) return 0; }
return 1; }
int main() { int i, val, sum[256], sc; char str[16];
freopen("e:\\test\\in.txt", "r", stdin);
for (i = 0; i < 26; i++) map[key[i]] = score[i]; scanf("%s", str); for (i = 0; str[i]; i++) col[str[i]]++;
cnt = 1; for (i = 'a'; i <= 'z'; i++) if (col[i]) { idx[i] = cnt; mul[cnt] = tot[cnt] = col[i] + 1; cnt++; }
mul[0] = 1; for (i = 1; i < cnt; i++) mul[i] *= mul[i - 1]; top = mul[cnt - 1]; hash[0] = 1;
while (scanf("%s", str), str[0] != '.') { for (i = 'a'; i <= 'z'; i++) sum[i] = 0; sc = 0; val = 0; for (i = 0; str[i]; i++) { sum[str[i]]++; if (sum[str[i]] > col[str[i]]) break; sc += map[str[i]]; val += mul[idx[str[i]] - 1]; } if (str[i]) continue; for (i = top; i >= 0; i--) { if (!hash[i]) continue; if (can_add(i, val) && hash[i + val] < hash[i] + sc) hash[i + val] = hash[i] + sc; } }
sc = 0; for (i = top; i >= 0; i--) if (hash[i] > sc) sc = hash[i]; printf("%d\n", sc - 1);
return 0; }
#include <stdio.h>
#define MOD(x) (((x) + 11380) % 11380)
int L1, L2, L3, D, f[11][11][11][31];
inline int part(int ma, int mb, int mc, int md, int a, int b, int c ) { return MOD(f[a][b][c][md - 1] * f[ma - a][mb - b][mc - c][md]); }
inline int calc(int ma, int mb, int mc, int md) { int a, b, c, r;
if (!ma && !mb && !mc) return 1;
r = 0; if (mc) { for (c = 0; c <= mc - 1; c++) r = MOD(r + part(ma, mb, mc - 1, md, 0, 0, c)); } if (mb) { for (b = 0; b <= mb - 1; b++) for (c = 0; c <= mc; c++) r = MOD(r + part(ma, mb - 1, mc, md, 0, b, c)); } if (ma) { for (a = 0; a <= ma - 1; a++) for (b = 0; b <= mb; b++) for (c = 0; c <= mc; c++) r = MOD(r + part(ma - 1, mb, mc, md, a, b, c)); } return r; }
int main() { int a, b, c, d;
freopen("e:\\in.txt", "r", stdin);
scanf("%d%d%d%d", &L1, &L2, &L3, &D);
f[0][0][0][0] = 1; for (d = 1; d <= D; d++) for (a = 0; a <= L1; a++) for (b = 0; b <= L2; b++) for (c = 0; c <= L3; c++) f[a][b][c][d] = calc(a, b, c, d);
printf("%d\n", D ? MOD(f[L1][L2][L3][D] - f[L1][L2][L3][D - 1]) : MOD(f[L1][L2][L3][D]) );
return 0; }
思路: 把括号的嵌套看成是一棵树就简单点了。 这棵树的最大深度为 D。()节点下面不能有{}[]节点,[]节点下面不能有{}节点。 然后我们从上往下依次摆放节点。 考虑只有()节点的情况。 如果 f[n][d] 表示现在有n个节点需要摆放,深度小于等于d。 那么当前节点的下面可以摆 1,2 ... n 个节点。 摆完当前节点之后,剩下的在右边继续摆。 总方案数就是等于 下面的方案数*右边的方案数 考虑三种节点都有的情况,实际上只是比上面的问题复杂一点点而已。 如果 f[a][b][c][d] 表示现在有a个{}节点,b个[]节点,c个()节点需要摆放。 当前节点摆 () 的时候,下面就只能摆 (),其余的全放在右边。 当前节点摆 [] 的时候,下面就只能摆 ()[],。。。 。。。 这题的复杂度是 O(L1*L1*L2*L2*L3*L3*D)。 看上去比较大,但是可以AC的~ 之前自己想的方法是 f[a][b][c][d] 表示深度等于d的方案数,而不是小于。 最后答案为 f[L1][L2][L3][D]。 复杂度多乘了一个D,就TLE了。 后来看了别人方法,发现保存深度小于等于d,这样的话会好一些。 最后答案为 f[L1][L2][L3][D] - f[L1][L2][L3][D - 1] 这方法实在牛逼! 代码:
下面这个小程序,可以演示 KMP 的匹配过程。 一直没有真正理解这个算法,昨天看了下算法导论。才懂了。 确实是一个相当精辟,相当巧妙的算法。太牛逼了!
#include <stdio.h> #include <string.h> #define MAX_LEN 256 int tbl[MAX_LEN]; char *str = "abbbbbbb"; char *ptn = "bb"; char spc[MAX_LEN]; int main() { int i, j; memset(spc, ' ', sizeof(spc)); tbl[0] = 0; for (i = 1; ptn[i]; i++) { for (j = tbl[i - 1]; j && ptn[j] != ptn[i]; j = tbl[j]); if (ptn[j] == ptn[i]) j++; tbl[i] = j; } printf("table:\n"); for (i = 0; ptn[i]; i++) printf(" %c", ptn[i]); printf("\n"); for (i = 0; ptn[i]; i++) printf("%3d", tbl[i]); printf("\n"); j = 0; for (i = 0; str[i]; ) { printf("\nmatching #%d\n", i); printf("%s\n", str); fwrite(spc, 1, i - j, stdout); printf("%s\n", ptn); fwrite(spc, 1, i, stdout); printf("^\n"); if (str[i] == ptn[j]) { i++; j++; printf("ok\n"); } else { if (j) j = tbl[j - 1]; else i++; printf("jumped to %d\n", j); } if (!ptn[j]) printf("matched at %d\n", i); } return 0; }
输出: table: b b 0 1
matching #0 abbbbbbb bb ^ jumped to 0
matching #1 abbbbbbb bb ^ ok
matching #2 abbbbbbb bb ^ ok matched at 3
matching #3 abbbbbbb bb ^ jumped to 1
matching #3 abbbbbbb bb ^ ok matched at 4
matching #4 abbbbbbb bb ^ jumped to 1
matching #4 abbbbbbb bb ^ ok matched at 5
matching #5 abbbbbbb bb ^ jumped to 1
matching #5 abbbbbbb bb ^ ok matched at 6
matching #6 abbbbbbb bb ^ jumped to 1
matching #6 abbbbbbb bb ^ ok matched at 7
matching #7 abbbbbbb bb ^ jumped to 1
matching #7 abbbbbbb bb ^ ok matched at 8
二十年前的那碗馄饨
这天,白云酒楼里来了两位客人,一男一女,四十岁上下,穿着不俗,男的还拎着一个旅行包,看样子是一对出来旅游的夫妻。
服务员笑吟吟地送上菜单。男的接过菜单直接递女的,说:“你点吧,想吃什么点什么。”女的连看也不看一眼,抬头对服务员说:“给我们来碗馄饨就行了!”
服务员一怔,哪有到白云酒楼吃馄饨的?再说,酒楼里也没有馄饨卖啊。她以为自己没听清楚,不安的望着那个女顾客。女人又把自己的话重复了一遍,旁边的男人这时候发话了:“吃什么馄饨,又不是没钱?”
女人摇摇头说:“我就是要吃馄饨!”男人愣了愣,看到服务员惊讶的目光,很难为情地说:“好吧。请给我们来两碗馄饨。”
“不!”女人赶紧补充道,”只要一碗!”男人又一怔,一碗怎么吃?女人看男人皱起了眉头,就说:“你不是答应的,一路上都听我的吗?”
男人不吭声了,抱着手靠在椅子上。旁边的服务员露着了一丝鄙夷的笑意,心想:这女人抠门抠到家了。上酒楼光吃馄饨不说,两个人还只要一碗。她冲女人撇了撇嘴:“对不起,我们这里没有馄饨卖,两位想吃还是到外面大排挡去吧!”
女人一听,感到很意外,想了想才说:“怎么会没有馄饨卖呢?你是嫌生意小不愿做吧?”
这会儿,酒楼老板张先锋恰好经过,他听到女人的话,便冲服务员招招手,服务员走过去埋怨道:“老板,你看这两个人,上这只点馄饨吃,这不是存心捣吗?”
店老板微微一笑,冲她摆摆手。他也觉得很奇怪:看这对夫妻的打扮,应该不是吃不起饭的人,估计另有什么想法。不管怎样,生意上门,没有往外推的道理。
他小声吩咐服务员:“你到外面买一碗馄饨回来,多少钱买的,等会结帐时多收一倍的钱!”说完他拉张椅子坐下,开始观察起这对奇怪的夫妻。
过了一会,服务员捧回一碗热气腾腾的馄饨,往女人面前一放,说:“请两位慢用。”
看到馄饨,女人的眼睛都亮了,她把脸凑到碗面上,深深地细了一口气,然后,用汤匙轻轻搅拌着碗里的馄饨,好象舍不得吃,半天也不见送到嘴里。
男人瞪大眼睛看者女人,又扭头看看四周,感觉大家都在用奇怪的眼光盯着他们,顿感无地自容,恨恨地说道:“真搞不懂你在搞什么,千里迢迢跑来,就为了吃这碗馄饨?”
女人抬头说道:“我喜欢!”
男人一把拿起桌上的菜单:“你爱吃就吃吧,我饿了一天了,要补补。”他便招手叫服务员过来,一气点了七八个名贵的菜。
女人不急不慢,等男人点完了菜。这才淡淡地对服务员说:“你最好先问问他有没有钱,当心他吃霸王餐。”
没等服务员反应过来,男人就气红了脸:“放屁!老子会吃霸王餐?老子会没钱?”他边说边往怀里摸去,突然”咦”的一声:“我的钱包呢?”他索性站了起来,在身上又是拍又是捏,这一来竟然发现手机也失踪了。男人站着怔了半晌,最后将眼光投向对面的女人。
女人不慌不忙地说道:“迩别瞎忙活了,钱包和手机我昨晚都扔到河里了。”
男人一听,火了:“你疯了!”女人好象没听见一样,继续缓慢的搅拌着碗里的馄饨。男人突然想起什么,拉开随身的旅行包,伸手在里面猛掏起来。
女人冷冷说了句:“别找了,你的手表,还有我的戒指,咱们这次带出来所有值钱的东西,我都扔河里了。我身上还有五块钱,只够买这碗馄饨了!”
男人的脸刷地白了,一屁股坐下来,愤怒的瞪着女人:“你真是疯了,你真是疯了!咱们身上没有钱,那么远的路怎么回去啊?”
女人却一脸平静,不温不火地说:“你急什么?再怎么着,我们还有两条腿,走着走着就到家了。”
男人沉闷的哼了一声。女人继续说道:“二十年前,咱们身上一分钱也没有,不也照样回到家了吗?那时侯的天。比现在还冷呢!”
男人听了这句,不由的瞪直了眼:“你说,你说什么?”女人问:“你真的不记得了?”男人茫然的摇摇头。
女人叹了口气:“看来,这些年身上有了几个钱,迩就真的把什么都忘了。二十年前,咱们第一次出远门做生意,没想到被人骗了个精光,连回家的路费都没了。经过这里的时候,你要了一碗馄饨给我吃,我知道,那时候你身上就剩下五毛钱了……”
男人听到这里,身子一震,打量了四周:“这,这里……”女人说:“对,就是这里,我永远也不会忘记的,那时它还是一间又小又破的馄饨店。”
男人默默地低下头,女人转头对在一旁发愣的服务员道:“姑娘,请给我再拿只空碗来。”
服务员很快拿来了一只空碗,女人捧起面前的馄饨,拨了一大半到空碗里,轻轻推到男人面前:“吃吧,吃完了我们一块走回家!”
男人盯着面前的半碗馄饨,很久才说了句:“我不饿。”女人眼里闪动着泪光,喃喃自语:“二十年前,你也是这么说的!”说完,她盯着碗没有动汤匙,就这样静静地坐着。
男人说:“你怎么还不吃?”女人又哽咽了:“二十年前,你也是这么问我的。我记得我当时回答你。要吃就一块吃,要不吃就都不吃,现在,还是这句话!”
男人默默无语,伸手拿起了汤匙。不知什么原因,拿着汤匙的手抖得厉害,舀了几次,馄饨都掉下来。最后,他终于将一个馄饨送到了嘴里,使劲一吞,整个都吞到了肚子里。当他舀第二个馄饨的时候,眼泪突然”叭嗒`叭嗒”往下掉。
女人见他吃了,脸上露出了笑容,也拿起汤匙开始吃。馄饨一进嘴,眼泪同时滴进了碗里。这对夫妻就这和着眼泪把一碗馄饨分吃完了。
放下汤匙,男人抬头轻声问女人:“饱了么?”
女人摇了摇头。男人很着急,突然他好象想起了什么,弯腰脱下一只皮鞋,拉出鞋垫,手往里面摸,没想到居然摸出了五块钱。他怔了怔,不敢相信地瞪着手里的钱。
女人微笑的说道:“二十年前,你骗我说只有五毛钱了,只能买一碗馄饨,其实呢,你还有五毛钱,就藏在鞋底里。我知道,你是想藏着那五毛钱,等我饿了的时候再拿出来。后来你被逼吃了一半馄饨,知道我一定不饱,就把钱拿出来再买了一碗!”顿了顿,她又说道,”还好你记得自己做过的事,这五块钱,我没白藏!”
男人把钱递给服务员:“给我们再来一碗馄饨。”服务员没有接钱,快步跑开了,不一会,捧回来满满一大碗馄饨。
男人往女人碗里倒了一大半:“吃吧,趁热!”
女人没有动,说:“吃完了,咱们就得走回家了,你可别怪我,我只是想在分手前再和你一起饿一回。苦一回!”
男人一声不吭,低头大口大口吞咽着,连汤带水,吃得干干净净。他放下碗催促女人道:“快吃吧,吃好了我们走回家!”
女人说:“迩放心,我说话算话,回去就签字,钱我一分不要,你和哪个女人好,娶个十个八个,我也不会管你了……”
男人猛地大声喊了起来:“回去我就把那张离婚协议书烧了,还不行吗?”说完,他居然号啕大哭,”我错了,还不行吗?我脑袋抽筋了,还不行吗?”
女人面带笑容,平静地吃完了半碗馄饨,然后对服务员:“姑娘,结帐吧。”,
一直在旁观看的老板张先锋猛然惊醒,快步走了过来,挡住了女人的手,却从身上摸出了两张百元大钞递了过去:“既然你门回去就把离婚协议书烧了,为什么还要走路回家呢?”
男人和女人迟疑地看着店老板,店老板微笑道:“咱们都是老熟人了,你门二十年前吃的馄饨,就是我卖的,那馄饨就是我老婆亲手做的!”说罢,他把钱硬塞到男人手中,头也不回地走了……
店老板回到办公室,从抽屉取出那张早已拟好的离婚协议书,怔怔地看了半晌,喃喃自语地说:“看来,我的脑袋也抽筋了……”
分手时想想以前,那个陪你甘苦与共的人,一路走来。其实你们的故事并不短。时间慢慢过去,那些感动却一点一点封存。其实最疼你的人不是那个甜言蜜语哄你开心的人。也许就是在鞋底藏5元钱。在最后的时候把最后一点东西省着给你吃,却说不饿的人……
|
这题能不能贪心,是很难说的。。 因为没有能什么证明贪心是对的,但也没找到什么反例。 代码写出来,WA了。 但总觉得是对的,因为吗的实在找不到反例。 结果找到数据测了下,果然十个过了九个。。 你看,如果这是NOIP,跟满分都没啥区别的对吧。我已经满足了。。 没过的那组,比较大,肉眼看不出是啥问题。 想了下,给排序加多了一个判断,然后那组数据就过了。。 然后提交,吗的还上榜了。。无语。 这样做比较无耻,网上有人说用最大流做,不理解。下次想一想。 思路: 如果牛喜欢的种类个数小于 K,那这种牛是无法满足的。。 把牛按照喜欢的种类个数排序,先处理小的。 就是用组合数枚举每一种可能的 pizza 情况。 用 hash 保存这些情况。 代码:
#include <stdio.h> #include <stdlib.h>
#define HASH_SIZE 65536
struct cow_node { int val, cnt; };
int C, T, K, ans; struct cow_node cows[1024]; int hash[HASH_SIZE];
int cmp(const void *a, const void *b) { if (((struct cow_node *)a)->cnt == ((struct cow_node *)b)->cnt) return ((struct cow_node *)a)->val - ((struct cow_node *)b)->val; return ((struct cow_node *)a)->cnt - ((struct cow_node *)b)->cnt; }
inline int insert(int val) { int i, h;
h = val & (HASH_SIZE - 1); for (i = h + 1; i != h && hash[i] && hash[i] != val; ) i = (i + 1) & (HASH_SIZE - 1); if (i == h || hash[i] == val) return 0; hash[i] = val; return 1; }
inline int calc(struct cow_node *t) { int arr[32], map[32], i, j, val;
for (i = j = 0; i < 32; i++) if (t->val & (1 << i)) map[j++] = (1 << i); for (i = 0; i < K; i++) arr[i] = i; while (1) { val = 0; for (i = 0; i < K; i++) val |= map[arr[i]]; if (insert(val)) return 1; for (i = K - 1; i >= 0 && arr[i] == i + t->cnt - K; i--); if (i < 0) break; arr[i]++; for (i++; i < K; i++) arr[i] = arr[i - 1] + 1; }
return 0; }
int main() { int i, j, k;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d", &C, &T, &K); for (i = 0; i < C; i++) { scanf("%d", &cows[i].cnt); cows[i].val = 0; for (j = 0; j < cows[i].cnt; j++) { scanf("%d", &k); k--; cows[i].val |= 1 << k; } if (cows[i].cnt < K) { i--; C--; continue; } } qsort(cows, C, sizeof(cows[0]), cmp);
for (i = 0; i < C; i++) ans += calc(&cows[i]); printf("%d\n", ans);
return 0; }
思路: 先按棋盘上的值从大到小排序。 然后先解决值大的,再解决值小的。 注意路径的比较。应该很容易WA的。
#include <stdio.h> #include <algorithm>
using namespace std;
#define MAX_N 512
struct seq_node { short y, x; }; struct map_node { int val, cnt; struct map_node *pre; }; struct map_node map[MAX_N][MAX_N], *ans; struct seq_node seq[MAX_N*MAX_N]; int N;
int cmp_seq(const void *a, const void *b) { return map[((struct seq_node *)a)->y][((struct seq_node *)a)->x].val - map[((struct seq_node *)b)->y][((struct seq_node *)b)->x].val; }
inline int cmp_path(struct map_node *a, struct map_node *b) { while (a && b) { if (a->val != b->val) return a->val - b->val; a = a->pre; b = b->pre; } return 0; }
inline void update(struct map_node *a, int y2, int x2) { struct map_node *b = &map[y2][x2];
if (y2 < 0 || y2 >= N || x2 < 0 || x2 >= N) return ; if (b->val > a->val && (b->cnt > a->cnt || b->cnt == a->cnt && cmp_path(b, a->pre) < 0) ) { a->cnt = b->cnt; a->pre = b; } }
int main() { int x, y, i; struct seq_node *t; struct map_node *m;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &N); i = 0; for (y = 0; y < N; y++) { for (x = 0; x < N; x++) { scanf("%d", &map[y][x].val); seq[i].y = y; seq[i].x = x; i++; } } qsort(seq, N*N, sizeof(seq[0]), cmp_seq);
for (t = &seq[N*N - 1]; t >= seq; t--) { m = &map[t->y][t->x]; update(m, t->y - 2, t->x + 1); update(m, t->y + 2, t->x + 1); update(m, t->y - 2, t->x - 1); update(m, t->y + 2, t->x - 1); update(m, t->y - 1, t->x + 2); update(m, t->y + 1, t->x + 2); update(m, t->y - 1, t->x - 2); update(m, t->y + 1, t->x - 2); m->cnt++; if ((!ans || m->cnt > ans->cnt) || (m->cnt == ans->cnt && cmp_path(m, ans) < 0) ) ans = m; }
printf("%d\n", ans->cnt); while (ans) { printf("%d\n", ans->val); ans = ans->pre; }
return 0; }
思路:
差分约束是个很神奇的东西,能将下面这种类型的不等式约束问题: d[1] - d[2] <= a d[4] - d[7] <= b ... 求 d[x] 的最大/小值 转化为最短路径的问题,真吗的太牛逼了。 原理呢,就是一个图求出单源最短路径之后。 两点之间的关系必然是满足 d[u] + w[u,v] >= d[v] 的,没法再松弛了。
对于这道题,有两种约束: 1. 输入里指明的约束 2. d[1] <= d[2] <= d[3] .... <= d[N] 建立完这两种约束之后,然后spfa求一下最短路径。 如果有负环,就输出 -1 如果不连通,就输出 -2
#include <stdio.h>
#define MAX_DIS 1000000032 #define MAX_N 1024 #define MAX_EDGES 65536
struct edge_node { int i, w; struct edge_node *next; };
int N, ML, MD; struct edge_node edges[MAX_EDGES], *map[MAX_N]; int edges_cnt; int D[MAX_N], queue[MAX_N], head, tail, vis[MAX_N], tm, cnt[MAX_N];
inline void add_edge(int a, int b, int w) { struct edge_node *e = &edges[edges_cnt++];
e->i = b; e->w = w; e->next = map[a]; map[a] = e; }
inline int push(int i, int d) { if (d >= D[i]) return 0; D[i] = d; if (vis[i] == tm) return 0; vis[i] = tm; cnt[i]++; if (cnt[i] >= N) return 1; queue[tail++] = i; tail &= MAX_N - 1; return 0; }
inline int spfa() { int i; struct edge_node *e;
for (i = 1; i <= N; i++) D[i] = MAX_DIS; tm++; head = tail = 0; push(1, 0); while (head != tail) { i = queue[head++]; head &= MAX_N - 1; vis[i] = 0; for (e = map[i]; e; e = e->next) if (push(e->i, D[i] + e->w)) return 1; }
return 0; }
int main() { int a, b, w, i;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d", &N, &ML, &MD); while (ML--) { scanf("%d%d%d", &a, &b, &w); add_edge(a, b, w); } while (MD--) { scanf("%d%d%d", &a, &b, &w); add_edge(b, a, -w); } for (i = 2; i <= N; i++) add_edge(i, i - 1, 0);
if (spfa()) a = -1; else a = (D[N] == MAX_DIS) ? -2 : D[N]; printf("%d\n", a);
return 0; }
思路:
这题是暴力枚举,哈哈。 枚举每一种可能的字母分配情况。 然后再对每个单词求出按键序列以后,对按键序列进行 hash,用普通的字串hash函数即可。 最后就可以统计具有唯一按键序列的单词个数了。
这种做法还是相当快的,代码跑到了450ms 。是第一哦!
#include <stdio.h> #include <string.h>
#define MAX_D 1024 #define HASH_SIZE 65536
struct node { struct node *next; int val, cnt; };
int B, L, D; int map[256], ans[256], best; char dict[MAX_D][16]; struct node nodes[MAX_D], *hash[HASH_SIZE]; int nodes_cnt; int vis[HASH_SIZE], tm;
inline void calc() { int i, h, val, uni; char *s; struct node *t;
tm++; nodes_cnt = 0; for (i = 0; i < D; i++) { val = 0; for (s = dict[i]; *s; s++) val = val*31 + map[*s] + 'a'; h = val & (HASH_SIZE - 1); if (vis[h] != tm) { vis[h] = tm; hash[h] = NULL; } for (t = hash[h]; t; t = t->next) if (t->val == val) break; if (!t) { t = &nodes[nodes_cnt++]; t->val = val; t->cnt = 0; t->next = hash[h]; hash[h] = t; } t->cnt++; }
uni = 0; for (i = 0; i < nodes_cnt; i++) if (nodes[i].cnt == 1) uni++;
if (uni > best) { best = uni; memcpy(ans, map, sizeof(map)); } }
void dfs(int b, int l) { int i, cnt;
cnt = L - l - (B - b) + 1; for (i = 0; i < cnt; i++) map[l + 'A' + i] = b; if (b == B - 1) { calc(); return ; }
for (i = cnt; i >= 1; i--) dfs(b + 1, l + i); }
int main() { int i;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d%d", &B, &L, &D); for (i = 0; i < D; i++) scanf("%s", dict[i]); dfs(0, 0); printf("%d\n", best); for (i = 'A'; i < 'A' + L; i++) { if (ans[i] != ans[i - 1]) putchar('\n'); putchar(i); } putchar('\n');
return 0; }
思路:
f[倒数第几天][队列头的位置] = { 该状态下所产生的最大值 } 状态转移: f[i][j] = max { V[j + i]*(N - i) + f[i - 1][j], f[i - 1][j + 1] + V[j]*(N - i) }
代码:
#include <stdio.h>
inline int max(int a, int b) { return a > b ? a : b; }
int N, V[2048], f[2][2048], *cur, *pre;
int main() { int i, j;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d", &N); for (i = 0; i < N; i++) { scanf("%d", &V[i]); f[1][i] = V[i]*N; } for (i = 1; i < N; i++) { pre = f[i & 1]; cur = f[(i + 1) & 1]; for (j = 0; j < N - i; j++) cur[j] = max(pre[j] + V[j + i]*(N - i), pre[j + 1] + V[j]*(N - i)); } printf("%d\n", cur[0]);
return 0; }
思路:
这题完全不懂,看了这份大牛的解题报告 http://hi.baidu.com/winterlegend/blog/item/2411981e8fd0ed6bf724e45e.html 发现吗的太牛逼了!
首先二分图匹配,正常情况下,都是单个单个的点这样匹配、 但是像这道题,右边的点一个可以匹配好几个左边的点。也是用匈牙利算法,不过要做少少修改。
枚举答案的时候,有两种方法: 一个是移动区间的方法。复杂度O(B)。注意,只移动区间右边的时候不用重新匹配,只需要接着上次没匹配完的继续匹配就行了 一个是二分法。复杂度 O(B lgB) 由于数据的问题,这两种方法时间相差无几。在 16ms 和 32ms 之间。
#include <stdio.h>
struct barn { int link[1024], cnt, vis, cap; }; struct cow { int rank[32]; };
struct barn barns[32]; struct cow cows[1024]; int start, end, last_pos; int N, B, tm;
int dfs(int c) { int i, j; struct barn *b;
for (i = start; i <= end; i++) { b = &barns[cows[c].rank[i]]; if (b->vis == tm) continue; b->vis = tm; if (b->cnt < b->cap) { b->link[b->cnt++] = c; return 1; } for (j = 0; j < b->cap; j++) if (dfs(b->link[j])) { b->link[j] = c; return 1; } } return 0; }
inline void init() { int i;
for (i = 1; i <= B; i++) barns[i].cnt = 0; last_pos = 1; }
inline int match() { int i, j;
for (i = last_pos; i <= N; i++) { tm++; if (!dfs(i)) break; } last_pos = i; return i > N; }
int main() { int i, j, ans;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &N, &B); for (i = 1; i <= N; i++) for (j = 1; j <= B; j++) scanf("%d", &cows[i].rank[j]); for (i = 1; i <= B; i++) scanf("%d", &barns[i].cap); #if 0 // O(B) ans = B; start = end = 1; while (start <= B && end <= B) { init(); while (end <= B && !match()) end++; if (end - start + 1 < ans) ans = end - start + 1; start++; } #else // O(B*lgB) int l, r, m;
l = 1; r = B; while (l <= r) { m = (l + r) / 2; for (start = 1; start <= B - m + 1; start++) { end = start + m - 1; init(); if (match()) break; } if (start == B - m + 2) { // failed l = m + 1; } else r = m - 1; } ans = r + 1; #endif
printf("%d\n", ans);
return 0; }
|