csu 1774 慷慨的奖励[双向链表]

CSU 1774 慷慨的奖励

题意: 给定长度为N的0~9数字字符串,从中删掉D个字符,求能够构成的最大的数字。

思路:D个字符一个个的删,直到全部删完:

  1. 如果当前这个字符比下一个字符还小,删之, 即先删字符串的升序部分;
  2. 删完之后,如果删完的新序列前面又构成了升序,删掉前面的升序部分;
  3. 删到最末尾了,然后全部是降序了,那么毫无疑问,从末尾往前删;

这样算下来,负责度就是O(N)。

例如:

N=8,K=5, 序列为14231432。

  1. 首先删掉第一个1,得:4231432;
  2. 然后删掉第一个2,得:431432;
  3. 然后删掉第一个1,得:43432;
  4. 检测到之前新增了一个升序部分,向前删掉第一个3,得:4432;
  5. 一直遍历到最后,已经构成了一个降序序列了,还要删一个,那么就从末尾向前删,得:443,结束。
之前还有一个思路,但是一直都在TLE,这里也Mark一下,毕竟没有失败的思路,哪来正确的思路:将题意转化为从N个字符串中,依次选取N-D个数字,让其最大。

步骤是每次选择出当前的最高位:

例如:

N=8,K=5, 序列为14231432。

  1. 首先从前6个字符142314中(PS:因为最高位不可能是最后面两个数字)选出最大的数字:4, 下标为1;
  2. 然后从下标为2的字符开始,在23143选出最大的数字:4,记录下标为5;
  3. 然后从下标为5的字符开始,在32中选出最大的数字:3,结束。
选择区间最大的数字,我用的是线段树。线段树的建立需要Nlog(N), 查询是log(N),查询次数是N-D次。按道理来说,这个思路复杂度是Nlog(N),然而很不幸,还是超时了,GG;

/**
 *  思路一: 双向链表
 *  Author:XXW
 **/
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)

const int maxn = 1e5 + 5;

int N, D;
char str[maxn];
struct Node {
    int prior, rear;
} nodes[maxn];

void del(int x) {
    int a = nodes[x].prior;
    int b = nodes[x].rear;
    if(a == -1) {
        nodes[b].prior = -1;
    } else {
        nodes[a].rear = b;
    }
    if(b == -1) {
        nodes[a].rear = -1;
    } else {
        nodes[b].prior = a;
    }
}

int main() {
#ifndef ONLINE_JUDGE
    FIN;
#endif // ONLINE_JUDGE
    while(~scanf("%d %d", &N, &D) && N) {
        scanf("%s", str);
        nodes[0].prior = -1;
        nodes[0].rear = 1;
        nodes[N - 1].prior = N - 2;
        nodes[N - 1].rear = -1;
        for(int i = 1; i < N - 1; i++) {
            nodes[i].prior = i - 1;
            nodes[i].rear = i + 1;
        }
        int pos = 0, phead = 0, cnt = D;
        while(cnt > 0) {
            int np = nodes[pos].rear;
            if(np != -1 && str[pos] < str[np]) {
                del(pos);
                if(pos == phead) phead = np;
                pos = np;
                cnt --;
            } else if(np != -1 && str[pos] >= str[np]) {
                pos ++;
                continue;
            } else if(np == -1) {
                pos = nodes[pos].prior;
                del(nodes[pos].rear);
                cnt --;
            }
            int pp = nodes[pos].prior;
            while(cnt > 0 && pp != -1 && str[pos] > str[pp]) {
                del(pp);
                pp = nodes[pos].prior;
                cnt --;
            }
            if(pp == -1) phead = pos;
        }
        for(int i = 0, pos = phead; i < N - D; i ++) {
            printf("%c", str[pos]);
            pos = nodes[pos].rear;
        }
        puts("");
    }
    return 0;
}
/**
 *  思路二: 线段树求区间最值
 *  Author:XXW
 **/
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define lson            l, mid, rt << 1
#define rson            mid + 1, r, rt << 1 | 1

const int maxn = 1e5 + 5;

int N, D;
char str[maxn];
struct Node {
    int prior, rear;
} nodes[maxn];

char segTree[maxn << 2];
int pos[maxn << 2];

void build(int l, int r, int rt) {
    if(l == r) {
        segTree[rt] = str[l];
        pos[rt] = l;
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    if(segTree[rt << 1] >= segTree[rt << 1 | 1]) {
        pos[rt] = pos[rt << 1];
    } else {
        pos[rt] = pos[rt << 1 | 1];
    }
    segTree[rt] = max(segTree[rt << 1], segTree[rt << 1 | 1]);
}

char ch;
int _prev;

char query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) {
        if(ch < segTree[rt]) {
            ch = segTree[rt];
            _prev = pos[rt];
        }
        return segTree[rt];
    }
    int mid = (l + r) >> 1;
    char ret = -1;
    if(L <= mid) {
        ret = max(ret, query(L, R, lson));
    }
    if(R > mid) {
        ret = max(ret, query(L, R, rson));
    }
    return ret;
}


int main() {
#ifndef ONLINE_JUDGE
    FIN;
#endif // ONLINE_JUDGE
    while(~scanf("%d %d", &N, &D) && N) {
        scanf("%s", str + 1);
        D = N - D;
        build(1, N, 1);
        _prev = 0;

        for(int i = 1; i <= D; i++) {
            int l = _prev + 1, r = N - (D - i);
            ch = -1;
            char res = query(l, r, 1, N, 1);
            printf("%c", res);
        }
        puts("");
    }
    return 0;
}


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: CSU飞跃手册2019是一个以服务学生发展为宗旨的学生服务项目,帮助学生实现个人成长和职业发展目标。飞跃手册提供了诸如成功学、职业规划、就业技巧以及个人发展等方面的指导,帮助学生掌握自我认知、提升职场竞争力并构建良好的人脉关系。 飞跃手册2019包括三个模块。第一个模块着重讲解如何探索自我,明确职业目标和规划职业道路。这些关键的步骤帮助学生发现自己的职业特长和兴趣爱好,从而为个人职业发展做出决策和规划。 第二个模块提供了职业发展过程中的必备技能,如招聘面试技巧、简历写作、职场沟通、决策和问题解决能力,以及管理和领导力等。这些技能帮助学生在职场中不断学习和成长,并展示自己的优势。 第三个模块则介绍如何利用社会资源和人脉关系来推动个人职业发展。它包括了网络管理、人脉建立、社交礼仪和社会责任等方面的指导。 总之,CSU飞跃手册2019是一份应对职场挑战和工作生涯的指南, 帮助学生建立自己的职业目标并实现自我发展。它讲解了个人成长、职场技能、人脉力量和社会责任等方面的内容,为学生的成功创造了一个强有力的支撑体系。 ### 回答2: CSU飞跃手册2019是一本由中国海洋大学控制科学与工程学院发行的指导学生爆炸性训练和实习的手册。该手册将学生分为不同职业兴趣组,并提供了具体的介绍、岗位培训以及实习就业方案。 该手册还包括了部分参考书目、学术论文和个人成长方向的建议,在实习期间指导学生更好地发展个人能力和提高自己的竞争力。除此之外,该手册还有一些校内外的实践活动介绍和经验分享。这些活动涵盖了多个不同领域,包括科技、创业、环保和社会公益等。 该手册还提供了一些面试技巧和求职指南,帮助学生更好地应对就业市场的挑战。此外,该手册还鼓励学生通过网络平台和社会实践等多种途径积累人脉资源,增强自己的社交和交际技巧。 总的来说,CSU飞跃手册2019为学生提供了一个全面的指导工具,帮助他们更好地理解职业规划,提高个人素质,扩展职业视野并实现自我价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值