POJ 3617 Best Cow Line (字典序最小问题 & 贪心)

原题链接:http://poj.org/problem?id=3617

问题梗概:给定长度为 N 的字符串 S, 要构造一个长度为 N 的字符串 T。起初,T 是一个空串,随后反复进行下列任意操作。

  • S 的头部删除一个字符,加到 T 的尾部。
  • S 的尾部删除一个字符,加到 T的尾部。

    目的是要构造字典序尽可能小的字符串 T

    限制条件:

  • 1 \leqslant N \leqslant 2000
  • 字符串 S 只包含大写英文字母
  • 输出的字符串每 80 个字符进行一次换行

字典序是指从前到后比较两个字符串大小的方法。首先比较第 1 个字符,如果不同则第 1 个字符较小的字符串更小,如果相同则继续比较第 2 个字符......如此继续,来比较整个字符串的大小。

问题分析:从字典序的性质上看,无论 T 的末尾有多大,只要前面部分的较小就可以。所以我们可以试一下如下贪心算法:

  • 不断取 S 的开头和末尾中较小的一个字符放到 T 的末尾。

这个算法已经接近正确了,只是针对 S 的开头和末尾字符相同的情形还没有定义。在这种情形下,因为我们希望能够尽早使用更小的字符,所以就要比较下一个字符的大小。下一个字符也有可能相同,因此就有如下算法:

  • 按照字典序比较 S 和将 S 反转后的字符串 S^{'}
  • 如果 S 较小,就从 S 的开头取出一个文字,追加到 T 的末尾;
  • 如果 S^{'} 较小,就从 S 的末尾取出一个文字,追加到 T 的末尾;
  • 如果相同则取哪个都可以。

根据前面提到的性质,字典序比较类的问题经常能用得上贪心法。


代码如下:

#include <iostream>
#include <algorithm>
using namespace std;

const int MAX = 2000;
// 输入
int N;
char S[MAX + 1];

void solve() {
	// 剩余的字符串为 S[a], S[a+1],...., S[b]
	int a = 0, b = N - 1, flag = 0;
	
	while (a <= b) {
	
		// 将从左起和从右起的字符串比较
		bool left = false;
		for (int i = 0; a + i <= b; i++) {
			if (S[a + i] < S[b - i]) {
				left = true;
				break;
			}
			else if (S[a + i] > S[b - i]) {
				left = false;
				break;
			}
		} 
		
		if (left) cout << (S[a++]), flag++;
		else cout << (S[b--]), flag++;
		if (flag == 80) cout << endl, flag = 0;
	} 
	cout << endl;
} 

int main() {
	while (cin >> N) {
		for (int i = 0; i < N; i++)
			cin >> S[i];
			
		solve();
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章