CF1526 D. Kill Anton
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1526 D. Kill Anton相关的知识,希望对你有一定的参考价值。
题意:
给你一个由’A’,‘N’.‘T’,'O’四个字符组成的字符串b,现在要求你改变b的顺序得到a,使得a通过移动回到b的步数最多。
每次移动只能移动相邻两项
题解:
官方题解说:最佳情况为相同字符靠在一起
证明我也不清楚。。
证明可以看看这篇文章
按照官方题解的说法,将相同的字符排列在一起,一共就四种字符,那么也就是排列方式一共就24种(4!),我们直接暴力求出每种情况,然后求出其要移动的步数,取最大值
这个移动的步数咋求?
假设原先字符串是ANTON,下标依次是1,2,3,4,5,现在我们将其打乱成ATONN,原先的下标就成了1,3,4,2,5,那13425变回12345的步骤不就是其逆序对吗?所以对于每一种情况我们求其逆序对,然后保留最大值
这题挺好~
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[50];//记录字符个数
vector<int>id[50],c;//记录字符
string s;ll nxt;
void msort(int l,int r){//归并排序
if(l>=r)return;//区间元素小于1
int mid=l+r>>1;
msort(l,mid);//分
msort(mid+1,r);//分
int i=l,j=mid+1,k=0;
int t[r-l+1];
while(i<=mid&&j<=r){
if(c[i]<=c[j])t[k++]=c[i++];
else{//此时存在逆序对,在归并过程中记录逆序对的个数
t[k++]=c[j++];
nxt+=mid-i+1;//记录逆序对数
}
}
while(i<=mid)t[k++]=c[i++];
while(j<=r)t[k++]=c[j++];
for(i=l,k=0;i<=r;i++,k++)c[i]=t[k];//将t排好序的数复制到c中
}
int main()
{
ios_base::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
int r[5]={0,'A','N','O','T'};//通过函数枚举各种可能性;
memset(a,0,sizeof(a));//清空
id['A'-'A'].clear();
id['N'-'A'].clear();
id['O'-'A'].clear();
id['T'-'A'].clear();
cin>>s;
for(int i=0;i<s.size();i++){
a[s[i]-'A']++;//记录每个字母的个数
id[s[i]-'A'].push_back(i+1);
}
ll ans=0;
string as;
do{
c.clear();
nxt=0;//清零记录下一种情况的操作数
for(int i=1;i<=4;i++){//四个字符分别连续存入形成一个新的字符串
c.insert(c.end(),id[r[i]-'A'].begin(),id[r[i]-'A'].end());
}
/*
cout<<"c= ";
for(int i=0;i<c.size();i++)cout<<s[c[i]-1];
cout<<endl;
*/
msort(0,c.size()-1);
if(nxt>ans){
ans=nxt;
as="";
for(int i=1;i<=4;i++) as+=string(a[r[i]-'A'],(char)r[i]);//存入此时的最优字符串,即前面c的原串
}
}while(next_permutation(r+1,r+5));//对四个字符全排列的各种可能性;
if(ans!=0)cout<<as<<endl;
else cout<<s<<endl;//ans为0说明原字符串已经为最优解
}
return 0;
}
我还有看到一种写法,本质一样,它将转化后的字符串下标定为1,2,3,4…,根据这个将原字符串下标定义为nxt[j],然后跑逆序对,一样的
for(int i=0;i<len;i++){//将该情况的字符串转为数字
for(int j=0;j<=3;j++){
if(mp[s[i]]==nxt[j]){//每次打乱nxt
c[i]=j;
break;
}
}
}
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\\n",a,b);
typedef long long ll;
using namespace std;
//qdu打铁匠
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
return s*w;
}
const int maxn=2e5+9;
int nxt[]={0,1,2,3};
ll a[maxn];
ll c[maxn],b[maxn];
map<char,int>mp;
ll cnt=0;
void unite(int l,int mid,int r){
if(l>=r)return ;
unite(l,(l+mid)>>1,mid);
unite(mid+1,(mid+1+r)>>1,r);
int i=l,j=mid+1;
for(ll k=l;k<=r;++k){
if(j>r||i<=mid&&c[i]<=c[j]) b[k]=c[i++];
else b[k]=c[j++],cnt+=mid-i+1;
}
for(ll k=l;k<=r;++k) c[k]=b[k];
}
string s;
ll cal(int len){
for(int i=0;i<len;i++){//将该情况的字符串转为数字
for(int j=0;j<=3;j++){
if(mp[s[i]]==nxt[j]){//每次打乱nxt
c[i]=j;
break;
}
}
}
cnt=0;
unite(0,(len-1)>>1,len-1);//求逆序对
return cnt;
}
void solve(){
cin>>s;
string a,n,o,t;
for(int i=0;i<s.length();i++){
if(s[i]=='A')a+='A';
if(s[i]=='N')n+='N';
if(s[i]=='O')o+='O';
if(s[i]=='T')t+='T';
}
string ans=s;
ll sum=0;
do
{
ll now=cal(s.size());
if(now>sum){//得到更大的逆序对,即需要步数更多
sum=now;
ans.clear();
for(int i=0;i<=3;i++){
if(nxt[i]==0)ans+=a;
if(nxt[i]==1)ans+=n;
if(nxt[i]==2)ans+=t;
if(nxt[i]==3)ans+=o;
}
}
}while(next_permutation(nxt,nxt+1+3));
cout<<ans<<endl;
}
int main()
{
int t=read();
mp['A']=0;
mp['N']=1;
mp['T']=2;
mp['O']=3;
while(t--){
solve();
}
}
以上是关于CF1526 D. Kill Anton的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #723 (Div. 2)D. Kill Anton 贪心,逆序对个数
Codeforces Round #723 (Div. 2) D. Kill Anton (字符串构造,全排列)
D. Kill Anton(Codeforces Round #723 (Div. 2)题解)
D. Kill Anton(Codeforces Round #723 (Div. 2)题解)