【UVALive - 6922】Reverse Polish Notation【贪心】

题意:

给出一个后缀表达式,仅包含 a a + + a a 表示数字, + + 表示运算符。每次可以交换表达式中的两个字符位置,也可以在任意位置插入一个 a a 或一个 + + ,要求操作完后使得原表达式变成一个合法的后缀表达式,求最少操作次数。 ( 1 n 3 1 0 6 ) (1\leq n\leq 3*10^6)


思路:

首先我们需要把题意理顺,怎样的表达式叫做一个合法的表达式。即对于表达式中每一个 + + 号位置,其前缀 a a 的和大于前缀 + + 的和,且表达式的最后一个位置前缀 a a 的和等于前缀 + + 的和减 1 1

我们令 a a 1 1 ,令 + + 1 -1 ,则对于每一个 + + 号位置,满足前缀和大于 0 0 ,且整个序列和等于 1 1

首先我们先计算出最少需要加上多少个 a a ,或者加上多少个 + + ,如果需要加 + + ,则放到末尾。如果需要加上 a a ,则放到开头。放好需要的 a a + + 之和,可以满足整个序列的和为 1 1

然后我们来考虑整个序列中不合法的位置,即符号为 + + ,且前缀和小于等于 0 0 。我们令这个位置变得合法,只有两种操作,一种是在这个位置之前放 1 1 ,然后在末尾添上一个 1 -1 ;或者交换这个位置和后面的那个位置。然后我们会发现在这个序列中一定有一个最小值和次小值,如果最小值和次小值都不满足要求,则我们需要至少交换两次,或者在开头位置加上 1 1 ,末尾加上 1 -1 ,则最小和次小都会 + 1 +1

考虑到这个地方之后,我们可以发现要想让操作数最少,我们需要先通过在序列首加 a a 使得次小值合法,然后再通过不断交换最小值和其相邻字符,令最小值也变法,这种策略就可以达到操作数最少的目的。


总结:

这是一个中等难度的贪心问题,比赛时有一个地方考虑错了,导致贪心失败,最后也没有做出来,还是较为懊悔的。

感觉自己在贪心问题上还是比较欠缺的。我觉得主要的原因还是在于一是贪心题做的少了,二是在于不会证明自己的贪心策略,而这主要也在于贪心策略比较难以求证。

如果加强自己的贪心能力呢?目前的策略就是多做一些贪心问题,多总结一些贪心思路,还是在摸索的一个过程,到底可不可行还要走一步看一步。

今天这道贪心问题的一个小得到就是关于序列字符问题,能转成数字就转成数字,因为一般人都对数字更加敏感。


代码:

#include <bits/stdc++.h>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 3000000+10;
const db EPS = 1e-9;
using namespace std;

char s[N];
int sum[N];

int main()
{
	int _; scanf("%d",&_);
	rep(Ca,1,_){
		scanf("%s",s+1);
		int s1 = 0, s2 = 0;
		int len = strlen(s+1), ans = 0, base = 0;
		rep(i,1,len)
			if(s[i] == 'a') s1++;
			else s2++;
		if(s1 > s2) ans += s1-1-s2;
		else ans += s2+1-s1, base += s2+1-s1;
		sum[0] = base;
		int m1 = 1, m2 = 1;
		rep(i,1,len){
			sum[i] = sum[i-1]+(s[i]=='a'?1:-1);
			if(s[i] == 'a') continue;
			if(sum[i] < m1) m2 = m1, m1 = sum[i];
			else if(sum[i] < m2) m2 = sum[i];
		}
		if(m2 <= 0 && m1 <= 0)
			ans = ans+2*(1-m2)+(m2-m1);
		else if(m1 <= 0)
			ans = ans+1-m1;
		printf("Case %d: %d\n",Ca,ans);
	}
	return 0;
}
发布了244 篇原创文章 · 获赞 115 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/100044577