POJ 3617 Best Cow Line(贪心)

题目链接: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;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值