题目链接:http://poj.org/problem?id=3617
刷挑战的贪心部分的路上,发现自己学了一年啥也不会。。。贪心的单调性和大致证明我都做不到。。。
来说这道题,这道题,贪心是显然的,如果头和尾的字母不一样,那么肯定是选择字母较小的那个放入答案中。但是,难点在于,如果他们是一样的呢?我想了几种解决方案。
1,直接取头(or尾)的那个,但是这样显然是错误的,样例都跑不过去。
2,既然他们第一个相同,我们就比较他们的第二个,如果第二个相同,比较第三个。直到第N个不相同,较小的那方直接全部插入答案中。但是这样也是错误的。。。例如ACDECA,这样做的话我就直接把ACD插入到答案里面,但最优的结果明显是AACCDE。所以这种贪心也是不正确的。
3,结合第二个分析,那我选择AA相同的时候,比较下一层,如果下一层相同,如CC,我们判断A C的大小,A < C,所以直接把AA取掉。如果不相同,如CC和DE,我们选择最小的,D,判断和C的关系,C < D,所以我们把外层两个C先拿掉,否则先拿掉内层的……这里开始已经十分十分混乱了,感觉脑袋不够用,这要分支成许多许多情况。(这里我是真的太贪了,想一次性拿两个, 把问题复杂化。还是应该回到每次只想着拿一个,哪个最优的基础上来。)
4,正解,简洁的做法。直接从头到尾建立一个字符串S1,从尾到头建立一个字符串S2,如果S1 < S2,那么就取掉头部的字母,否则取掉尾部的字母。为什么这样的正确的呢?这就是一种贪心,当头和尾相同的时候,我们选择当前最优解,就是如果从头部建立的字符串字典序小于尾部,那么当前说明选择头部为最优解,否则选择尾部为最优解。就像ACDECA一样,我们建立S1 = ACDECA, S2 = ACEDCA,S1 < S2,所以取掉头部的A是最优的。
代码如下:(想改自己代码风格真是麻烦,不伦不类)
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
char str[2005], ch, ans[2005], str_1[2005], str_2[2005];
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
ch = getchar();
while(ch < 'A' || ch > 'Z')
ch = getchar();
str[i] = ch;
}
int head = 0, end = n - 1;
int p = 0;
while(head <= end)
{
if(str[head] != str[end]){
if(str[head] < str[end]){
ans[p++] = str[head];
head++;
}
else{
ans[p++] = str[end];
end--;
}
}
else{
int p1 = 0, p2 = 0;
for(int i = head; i <= end; i++)
str_1[p1++] = str[i];
str_1[p1++] = '\0';
for(int i = end; i >= head; i--)
str_2[p2++] = str[i];
str_2[p2++] = '\0';
if(strcmp(str_2, str_1) > 0){
ans[p++] = str[head];
head++;
}
else{
ans[p++] = str[end];
end--;
}
}
}
ans[p++] = '\0';
for(int i = 0; i < p - 1; i++){
if(i && i % 80 == 0)
cout << endl;
cout << ans[i];
}
cout << endl;
return 0;
}
这样写还是太糟糕了,花费了许多时间,看到正解的写法感觉还是妙的多。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
char str[2005], ch;
int n;
bool flag;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
ch = getchar();
while(ch < 'A' || ch > 'Z')
ch = getchar();
str[i] = ch;
}
int head = 0, end = n - 1, cnt = 0;
while(head <= end){
if(cnt && cnt % 80 == 0)
putchar('\n');
flag = false;
for(int i = 0; head + i <= end; i++){
if(str[head + i] < str[end - i]){
flag = 1;
break;
}
else if(str[head + i] > str[end - i]){
break;
}
}
if(flag)
putchar(str[head++]);
else
putchar(str[end--]);
cnt++;
}
putchar('\n');
return 0;
}