题解:Atcoder - ABCF - Best Concatenation(洛谷T_abc268_f[ABC268F] Best Concatenation)
·题目
Atcoder链接:F - Best Concatenation
洛谷链接:[ABC268F] Best Concatenation
·难度
算法难度:普及
思维难度:提高
调码难度:普及
综合评价:偏难
·算法
贪心+sort排序
·思路
对于每一个字符串s,有两个属性:‘X’的个数、所有数字的和。假设s[p`[1]]s[p`[2]]s[p`[3]]...s[p`[n]]为最优排列,我们任意找到两个连续字符串s[i],s[i+1]进行交换,在i-1及之前的字符串分数总和肯定不会比变化(s[i]与s[i+1]的变化不会影响到它们),i+2及之后的字符串的分数综合也不变(它们之前的x的个数【记作y】不变,一直是y[1]+y[2]+...+y[i]+y[i+1],里面的数字也不变),改变的只有s[i]与s[i+1]的分数。如果交换后比交换前整个字符串更优,则交换后比交换前s[i]s[i+1]更优。因此若要在原排列的基础上交换两个字符串s[i]与s[j](i<j)的排列顺序,必须要满足<s[i]的X个数>*<s[j]的数字之和>小于<s[j]的X个数>*<s[i]的数字之和>(这里消去了一些交换前与交换后不变的项,比如说s[i]与s[j]内部的分数总和)。
按照这种方式找出排列方式之后,依次连接统计分数即可(开long long)。
·代码
Atcoder链接:Submission #42998483 - AtCoder Beginner Contest 268 - F
洛谷链接:记录详情
#include<bits/stdc++.h>
#define N 220000
using namespace std;
string str[N]={};
long long ans=0;
int p[N]={},sn[N]={},sx[N]={},n=0,sum=0;
bool cmp(int x,int y);
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
p[i]=i;
cin>>str[i];
for(auto j:str[i]){
if(j=='X'){
sx[i]++;
}else{
sn[i]+=j-'0';
}
}
//统计‘x’总个数(sx)与数字总和(sn)
}
sort(p+1,p+1+n,cmp);
//按照以上规律排序,冒泡太慢采用快排
for(int i=1;i<=n;i++){
for(auto j:str[p[i]]){
if(j=='X'){
sum++;
}else{
ans+=sum*(j-'0');
}
}
}
//一次遍历最有排列后每个字符串并计算分数
printf("%lld\n",ans);
return 0;
}
bool cmp(int x,int y){
if(sn[y]*1LL*sx[x]>sn[x]*1LL*sx[y]){
//如果交换前的分数比交换后的分数高则不能交换,注意如果不加*1LL会炸int
return true;
}
//否则交换
return false;
}
·注意
①对于答案统计和排序cmp中分数计算,都要开longlong。
②‘X’其实是大写。
③不可以写冒泡排序,虽然看起来更合理。