【CF1841C】Ranom Numbers【暴力】【贪心】

文章讨论了一种关于字符串中字符替换的问题,提出在最多10个位置替换字符以最大化得分的方法。通过分析字符增减对得分的影响,证明了替换首次或最后出现的字符更为高效。文章还提供了使用贪心算法的C++代码实现这一策略。
摘要由CSDN通过智能技术生成

在这里插入图片描述
在这里插入图片描述

分析

其实本题中蕴含一个重要结论:最佳情况是最多考虑10个位置来替换:A的第一个和最后一个位置,B的第一个或最后一个位置,依此类推。

这样,我们只有50个不同的候选答案(10个位置每个位置可以替换成5个字母中的任意一个),我们可以在O(n)中检查它们中的每一个。

证明:当我们替换一个字符时,我们要么增加它,要么减少它。如果我们增加一个字符,很容易看出为什么只尝试每个字符第一次出现的位置即可——增加一个字符可能会影响它左边的一些字符(将它们从正转为负),不会影响右边的字符。通过选择一个字符第一次出现的位置,我们确保从正到负转换的字符数量尽可能少。注意,如果字符串至少有一个不同于E的字符,我们可以将第一个这样的字符替换为E,并将答案增加至少9000(这在证明的第二部分中很有用)。

现在假设减少一个字符是最优的,让我们展示一下,选择最后一个出现的字符来减少总是最优的。(将最后一个字符减少,比如把最后一个E减少,不会影响其右边的字符,只会影响其左边有一部分字符,即倒数第二个E和最后一个E之间的某些字符,使其权值由负转正。)假设我们减少了一个字符,而这不
是最后一次出现的位置。(如果修改的不是最后一个字符,则其左边和右边的其它字符都不会受到影响,只有该字符本身的权值会发生变化。)这意味着该字符在替换后将为负数,因此在替换之前应该为负数(这样对答案的影响尽可能小)。通过用另一个负数字符替换负数字符,我们可以为答案添加的最大值是999(对应将负数的D更改为负数的A),我们已经证明,通过用E替换字符串中的第一个非E字符,我们至少可以让答案增加9000。因此,如果我们减少一个字符,而它不是该字符的最后一次出现的位置,则这必定不是最优的。

所以,我们只要正反各做一次,记录每一个字符第一次出现(反着相当于最后一次),然后统计分数就行。

另外两种思路

在这里插入图片描述

上代码

贪心

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

int t,pos[11],ap[6],sum,ans; 

int main()
{
	cin>>t;
	while(t--)
	{
		int sum=0,ans=-2147483600;
		string a;
		cin>>a;
		int len=a.length();
		for(int i=0;i<=len-1;i++)
		{
			if(a[i]=='A'&&ap[1]==0) pos[1]=i,ap[1]=1;
			else if(a[i]=='B'&&ap[2]==0) pos[2]=i,ap[2]=1;
			else if(a[i]=='C'&&ap[3]==0) pos[3]=i,ap[3]=1;
			else if(a[i]=='D'&&ap[4]==0) pos[4]=i,ap[4]=1;
			else if(a[i]=='E'&&ap[5]==0) pos[5]=i,ap[5]=1;
		}
		memset(ap,0,sizeof(ap));
		for(int i=len-1;i>=0;i--)
		{
			if(a[i]=='A'&&ap[1]==0) pos[6]=i,ap[1]=1;
			else if(a[i]=='B'&&ap[2]==0) pos[7]=i,ap[2]=1;
			else if(a[i]=='C'&&ap[3]==0) pos[8]=i,ap[3]=1;
			else if(a[i]=='D'&&ap[4]==0) pos[9]=i,ap[4]=1;
			else if(a[i]=='E'&&ap[5]==0) pos[10]=i,ap[5]=1;
		}
		memset(ap,0,sizeof(ap));
		char mx='0';
		for(int i=len-1;i>=0;i--)
		{
			if(a[i]>=mx) 
			{
				mx=a[i];
				if(a[i]=='A') sum+=1;
				else if(a[i]=='B') sum+=10;
				else if(a[i]=='C') sum+=100;
				else if(a[i]=='D') sum+=1000;
				else if(a[i]=='E') sum+=10000;
			}
			else
			{
				if(a[i]=='A') sum-=1;
				else if(a[i]=='B') sum-=10;
				else if(a[i]=='C') sum-=100;
				else if(a[i]=='D') sum-=1000;
				else if(a[i]=='E') sum-=10000;
			}
		}
		ans=max(sum,ans);
		for(int k=1;k<=10;k++)
		{
			for(int j=1;j<=5;j++)
			{
				sum=0;mx='0';
				for(int i=len-1;i>=0;i--)
				{
					char t;
					if(pos[k]==i) t=char(j+64);
					else t=a[i];
					if(t>=mx) 
					{
						mx=t;
						if(t=='A') sum+=1;
						else if(t=='B') sum+=10;
						else if(t=='C') sum+=100;
						else if(t=='D') sum+=1000;
						else if(t=='E') sum+=10000;
					}
					else
					{
						if(t=='A') sum-=1;
						else if(t=='B') sum-=10;
						else if(t=='C') sum-=100;
						else if(t=='D') sum-=1000;
						else if(t=='E') sum-=10000;
					}
				}
				ans=max(ans,sum);
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值