重排字符串
解题思路:首先要明确,当有一串字符串之后,要想实现重排的话,就相当于把每个位置上的值重新填写,那么通过观察题目发现,能不能重排一定是取决于字符个数最多的值的。为什么呢? 看如下例子:a a b b c c c c c c;当你总共就只有10个数的时候,那么就是说你只需要把这字符最多的c解决了,a和b自然也就被解决了。重排的话那么就是10个位置都是空的,把c放进去,那么一定是每隔一个字符放一个c是最合理的,只需要浪费一个位置就能把c给隔开,如果你每隔两个字符放c,或者更多的话,这些情况无法保证一定能重排这些字符。还有一种情况就是无论怎么样都重排不了这些字符串,这种情况就是的当你最多的那个字符出现的个数超过了总个数的一半,那么是一定不可以重排的。注意这里只是说明了个数是偶数的情况,那么个数如果是奇数呢?比如a c c或者是a a b b c c c这个情况呢?所以判断是否能够重排的时候需要把个数+1/2,这样对于本来是个数是偶数的不会有任何的影响,而又能够很好地解决奇数的问题。重排的时候只需要把字符往已经处理完的个数最多的字符里面插就好了。
代码如下:
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
string s;
cin>>s;
int mem[26]={0};
char ret[n];
int maxCount=0;
char maxChar=0;
//先遍历一遍,把所有出现过的字符的个数统计一下。
//同时记录下最大的个数和字符是多少,方便后面重新排列
for(int i=0;i<n;i++)
{
int index=s[i]-'a';
if(++mem[index]>maxCount)
{
maxCount=mem[index];
maxChar=s[i];
}
}
//到这里就是判断能不能重排了
if(maxCount>(n+1)/2)
{
cout<<"no"<<endl;
}
else
{
cout<<"yes"<<endl;
int i=0;
//先排字符最多的
while(maxCount--)
{
ret[i]=maxChar;
i+=2;
}
//再排其他字符,只需要往里面放就可以了
for(int j=0;j<26;j++)
{
if(mem[j]&&j+'a'!=maxChar)
{
//开始放其他字符了
while(mem[j]--)
{
if(i>=n) i=1;
ret[i]=j+'a';
i+=2;
}
}
}
cout<<ret<<endl;
}
return 0;
}
删除相邻数字的最大分数
解题思路:先把每个值出现的总和求解出来,求解出来之后,用一个数组来记录他们的值。
为什么要这么做呢?这样可以避免在删除某个数之后,有反复的去遍历输入的数组。当用一个数组记录完了之后,你会惊奇的发现,这道题好像和打家劫舍非常相似。都是当选择了一个之后,他相邻的就不可以选了。这样就联想到了可以用dp来解决这道题了。用dp那么就一定要知道dp的几个关键要素:dp表示的含义,初始化,状态转移方程,以及遍历顺序。那么每个位置都有两种状态,一种是被选择了,另外一种就是没有被选择。那么这里用两个dp表来表示,f[i]表示i下标的位置一定被选了,g[i]就表示i下标的位置一定没被选。
代码如下:
#include <iostream>
#include <vector>
using namespace std;
const int N=1e4+10;
int sum[N];//sum[i]表示数组中i的个数的总和
int main()
{
//1.第一步就是先遍历数组,把所有值相同的加起来并且存起来
//这样的话,就不用在删除元素之后反复的遍历数组了
int n;
cin>>n;
vector<int> v(n,0);
for(int i=0;i<n;i++)
{
cin>>v[i];
sum[v[i]]+=v[i];
}
//sum中就存了v中每个元素的种和,这样就可以转换为,当在sum中选了i位置的值,那么相邻的就不可以选
//联想到了打家劫舍的那道题
int f[N];//f表示一定选了
int g[N];//g表示一定没选
f[0]=g[0]=0;
for(int i=1;i<N;i++)
{
f[i]=g[i-1]+sum[i];
g[i]=max(g[i-1],f[i-1]);
}
//输出结果一定就是最后一个数,选了和没选的最大值
cout<<max(f[N-1],g[N-1])<<endl;
return 0;
}
打家劫舍
解题思路:这是一道典型的dp的题目。如果不懂dp的小伙伴可以先去了解一下dp是什么,再来看这篇题解。这道题和上面那道删除相邻数字的最大分数类似,完全是一模一样的解法。这里就不做过多的解释了,直接给出代码。
class Solution {
public:
int rob(vector<int>& nums)
{
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
vector<int> dp(nums.size(),0);
//dp[i]表示偷前i间房屋能够偷到的最大值
dp[0]=nums[0];dp[1]=max(nums[0],nums[1]);
for(int i=2;i<nums.size();i++)
{
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.size()-1];
}
};
组队竞赛
解题思路:博主一上来想到的就是先排序。那么排完序之后再怎么处理呢?比如 1 2 3 4 8 9 12 13 15这个例子就是排完序之后的。要把这9个数分为三队,要求是输出一个整数表示所有队伍的水平总和的最大值。那这个时候就想到了贪心的算法了,整体总和最大,那么我能不能保证我每次选到的值都是我能够选的里面的最大值呢?答案是可以的。第一队从9个数里面选3个数,第二队从剩下的6个数里面选3个,第3队就只能选最后剩下的三个数。那么怎么样保证我在选第一队的时候,选到水平值最大的呢?那么我肯定是选择一个最大的和一个最接近最大的,然后另外一个就可以随便选了,但是这个时候你会选择什么呢?聪明的人肯定是选择最小的把,因为只有这样才有可能不会影响后面队去选择在他们能够选择的数据里面选出比较大的值。
代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin>>n;
vector<long long> v(3*n,0);
for(int i=0;i<3*n;i++)
{
cin>>v[i];
}
sort(v.begin(),v.end());
int i=3*n-2;
long long sum=0;
for(int j=0;j<n;j++)
{
sum+=v[i];
i-=2;
}
cout<<sum<<endl;
return 0;
}
笔试集训4就到这里啦,有疑问的小伙伴欢迎来私信博主哟,一起加油一起进步!