【代码超详解】POJ 3617 Best Cow Line(贪心)

博客详细解析了POJ 3617 Best Cow Line问题的算法,通过贪心策略进行牛的重新排列,确保按字典序排序。介绍了如何使用两个光标模拟过程,以及处理ASCII值相等情况的策略,最后提供了AC代码。
摘要由CSDN通过智能技术生成

一、题目描述
FJ 带着他的牛去参加一年一度的 Farmer of the Year 评选。每头牛都有一个名字,注册时按牛的队列顺序取首字母进行注册。例如: Bessie, Sylvia, and Dora 三头牛的注册结果是 BSD 。而后,根据字典序来完成判定。
现在 FJ 已经来不及返回农场给牛重新整队了,为了能够尽快参评,他决定在注册前夕当场重排他的牛。他开了一条新队,然后每次只将剩余的牛的头或尾移到新队列中。
输入第一行单个数字 N ,1 <= N <= 2000 。
接下来 N 行每行一个字母,代表牛的名字的首字母。
输出按上述说明排序后的队列。每 80 个字符换一行。

二、算法分析说明与代码编写指导
以样例为例:
Sample Input
6
A
C
D
B
C
B
Sample Output
ABCBCD
首先看头尾,A比D小,先把A放入新队列中,然后A的光标移到C。
B比C小,把B移到新队列的队尾,然后B的光标移到C。
两边都是C,那么看下一位。B比D小,两边的光标都退回到C,然后把靠原队列队尾的C放到新队列的队末,相应的光标从C移到B。
B比C小,B移到新队队末,B处的光标移到D。
C比D小,C移到新队队末,C处的光标移到D。
两个光标重合,D放到队尾,结束。
推广后:
由于 N <= 2000 ,我们可以放心地模拟全过程,不用担心超时。
注意每行只有一个字母,如果用

scanf("%s", s);

来试图一次性读取完毕,肯定不能成功。所以应该一个一个字符读入。但是注意处理回车。所以开始读取字母时,应该先用一次

getchar();

吞掉回车,然后再读取下一个字母。
用两个光标 i 和 j 来读取原队列的队首和队末。c 记录原来的字符串。p 表示已经输出的字符个数。
每次输出,都要检验是否已经可以换行。
如果 c[i] < c[j] ,就输出队首的 c[i] ,然后 i 往后移一格,并 ++p 。否则 j 往前移一格,并 ++p 。
如果 i 和 j 位置的字符相等,那么就要额外处理:
首先自然能想到把 i 和 j 都靠内移动,直到两个字符不同,考察哪边字符的 ASCII 更小,然后将光标恢复原位,把能够输出更小字典序的那一侧的字符先输出。
光标恢复原位,可以通过在第一次判定两边相等以后用变量 i1 和 j1 分别保存当前的 i 和 j ,在需要恢复的时候复制回去即可。
将光标移动到两个字符不同为止,可以通过循环:

while (c[++i] == c[--j]) {
   }

来完成。移动到两个字符不同以后只需要补上恢复光标的代码,其余与未找到相同字符的情况一致:哪边的字符小,输出哪边。
但是有一种比较特殊的情况:我们知道 i 和 j 分别是从队首和队末向中间移动的,如果 i 和 j 重合或互相越过了,就证明所有的字符已经输出完毕,应该退出循环。但是上述循环体并没有检测 i 和 j 是否重合或变成 i > j 的语句。也就是说,可能存在 i 不停增大而 j 不停减小导致越界的风险。所以在这里我们应该将循环体改成:

while (c[i] ==
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值