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

来源:互联网 发布:金蝶房地产软件 编辑:程序博客网 时间:2024/05/18 01:45

CSU 1774 慷慨的奖励

题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=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 | 1const 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 0
原创粉丝点击