|
思路:
它的要求是,给你几个字母,用这些字母拼几个字典里面有的单词,所有单词加起来求最高得分。 转化一下,就是个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;
}

|