//主要算法思路是贪心,但是贪心规则比较难想明白,所以有了难度(反正我一开始也不会的,是看了大佬的思路)
//1.先确定所需要的字母个数zimushu(int), 确定所需字母个数方法比较简单,就是给定一个长度一定字符串最多的冒泡次数
// (这还将在后面用到,不妨将其命名为"贪1")而这也是另一种贪心策略了即往里面插数,往字符串里插入一个字母x,可以达到的最大冒泡数,即字符串中的字符数减去(-)与字符串中与x相同的个数
//2.在确定长度后,就开始主贪心策略了,从头到尾贪心
// 主贪心策略的方法为:给定一个固定的开头(前缀)在剩余的确定的字符位数下使得冒泡次数最多(很多人估计可以想到这种方法,但是碍于不会实现而放弃这种思路)
// 可以先说明这种方法开头从'a'往'z'贪心,处理出来的结果一定是字典序最小的
// 下面说说如何贪心:
// 我们可以事先开一个数组存储前缀的26个字母的个数sum[26],和前缀的冒泡次数(代码实现时我用的是now+addtonow)
// 然后就是思考如何处理给定前缀,给定后续字符数目后,进行贪心:
// 是不是觉得与先前确实字母个数的贪心有一部分相似,我一开始就是觉得多了前缀好像不能贪心处理了一样
// 但是我们仔细想想,是可以处理的:
// 想把前缀遮住,然后往后缀(即我们要来处理的后续字符串)中一个一个插入字符,最大的冒泡数应该是后缀的字符数减去(-)与后缀中与x相同的个数为i-tempsum[j](对,就是"贪1")
// 现在揭开前缀,则贪心加入一个字符x的最大冒泡数为temp1(sum[0]~sum[x-1]的和)+ i-tempsum[j]=max
// 然后不断贪心累加max就可以得到给定一个固前缀在剩余的确定的字符位数下冒泡的最大次数为addtonow2
// 再将now+addtonow+addtonow2与V比较(不断随缩短他们的差距)
// 主贪心进行到最后一位结束时now+addtonow+addtonow2将与V相等
// 其中原因是你从'a'往'z'进行主贪心,是冒泡是增长大贪心,具体内涵你自行写一下代码就理解了
//数据100w以下都是非常快的
#include<bits/stdc++.h>
using namespace std;
string strr;
int V,sum[26];
int zimushu;
int now;
int addtonow,addtonow2;
//addtonow比zimuf大的字母数
//addtonow2在加上yushu个字母后冒泡增加的最大次数
bool qiuyumax(int zimuf,int yushu){
addtonow=0;addtonow2=0;
for(int i=zimuf+1;i<26;i++){
addtonow+=sum[i];
}
sum[zimuf]++;
int tempsum[26];
memset(tempsum,0,sizeof(tempsum));
for(int i=0;i<yushu;i++){
int max=-1,temp1=0,ops=0;
for(int j=25;j>=0;j--){
if(i-tempsum[j]+temp1>max){
max=i-tempsum[j]+temp1;
ops=j;
}
temp1+=sum[j];
}
addtonow2+=max;
tempsum[ops]++;
}
if(now + addtonow + addtonow2 >= V) {
now += addtonow;
return true;
}
else {
sum[zimuf] -- ;
return false;
}
}
void getlen(){
int temp1=0,temp2=0,temp3=0;
while(1){
temp2=temp3;
temp3+=(2*temp1+25)*13;
if(temp3>=V) break;
temp1+=25;
}
int shu=temp1/25*26;//最后需要的字母个数
for(;temp2<V;temp1++){
temp2+=temp1;
shu++;
}
// cout<<"shu="<<shu<<endl;
zimushu=shu;
}
int main()
{
cin>>V;
getlen();
for(int i=0;i<zimushu;i++){
for(int j=0;j<26;j++){
if(qiuyumax(j,zimushu-i-1)){
string s;
s.push_back(j+'a');
strr=strr+s;
break;
}
}
}
cout<<strr;
return 0;
}
0.0分
3 人评分