CSP 2019.11.14 密码

题目描述

哪里有压迫,哪里就有反抗。
moreD 的宠物在法庭的帮助下终于反抗了。作为一只聪明的宠物,他打算把
魔法使 moreD 的魔法书盗去,夺取 moreD 的魔法能力。但 moreD 怎么会让自己的
魔法书轻易地被盗取?moreD 在魔法书上设置了一个密码锁,密码锁上有一个问
题。
施以斯卧铺魔法吧,你有 M 次机会,如此将得完美密码。
然后是一串小写字母串。
moreD 的宠物斯卧铺魔法就是施法时的字符串其中相邻两位交换。
而 moreD 对于完美密码的定义自然是最小字典序了。
请帮助 moreD 的宠物,想出密码吧。

输入格式

第一行一个整数 M,表示操作次数。
第二行一串小写字母组成的字符串 S,如题目所示。

输出格式

输出完美密码

输入样例

3
dcba

输出样例

adcb

数据范围

对于 30%的数据|S|≤10
对于 60%的数据|S|≤3,000
对于 100%的数据 8≤|S|≤100,000,M≤(|S|-8)^2+2

后记

宠物最终战胜了 moreD,和自己的宠物快乐地生活着。

样例解释

先对第 3,4 两位施法,字符串变成 dcab,然后对第 2,3 两位施法,字符串
变成 dacb,最后对第 1,2 两位施法,字符串变成 adcb。

一眼望过去

很明显是个贪心啊,要求在一定的步骤内通过两两交换使得字典序最小.

解法

针对30%的数据:
暴力dfs。
针对60%的数据:
贪一下心吧。由于题目要求字典序,可以贪心地从前到后确定每一位的字母。字母肯定是从小到大地枚举,操作距离内的枚举字母的话肯定会贪心地把这个字母换入目标位置。而选择枚举的字母就是贪心地选择尽量前的字母。
针对100%的数据:
每次找每种字母最前的那个,再对他进行往前移,这样可以用最小的步骤。可以用vector或链表。然后我们要统计交换的代价,因为是动态改变的,我们就可以用数据结构来维护,比如树状数组或线段树.

参考代码

#include<bits/stdc++.h>
const int N=1e5+10;
typedef long long ll;
using namespace std;
int n;
int top[26];
ll m,tre[N];
char s[N],ans[N];
vector<int>f[26];
inline int lowbit(int k){
    return k&(-k);
}
inline void add(int k,int v) {
    for(int i=k; i<=n; i+=lowbit(i)) tre[i]+=v;
}
inline ll ask(int i) {
    ll sum=0;
    while(i) {
        sum+=tre[i];
        i-=lowbit(i);
    }
    return sum;
}
int main() {
    freopen("pasuwado.in", "r", stdin);
    freopen("pasuwado.out", "w", stdout);
    cin>>m;
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1; i<=n; i++) {
        f[s[i]-'a'].push_back(i);
        if(i==1) add(i,0);
        else add(i,1);
    }
    for(int i=1; i<=n; i++) {
        for(int j=0; j<26; j++) {
            if(top[j]>=f[j].size()) continue;
            int t=f[j][top[j]];
            ll cost=ask(t);
            if(cost<=m) {
                m-=cost;
                add(t+1,-1);
                top[j]++;
                ans[i]=j+'a';
                break;
            }
        }
    }
    for(int i=1; i<=n; i++) printf("%c",ans[i]);
    cout<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Fast-Bird/p/11856547.html
CSP