洛谷普及B3694 数列离散化

题目:数列离散化

题号:B3694

难度:普及一


题目分析

这种题方法就放在明面上,问题是如何优化内存和时间,枚举,排序,去重,比较。

但是我还是找到了一种针对该题更精妙的方法,不用枚举比较出满足条件的个数,但需要两次排序

思路

首先找到题中的任意一行例子

1 6 2 2 7  -->  1 3 2 2 4  分析其中的规律关系,

题中的意思是,比当前数小的数的个数加一。

然后每个数都对应着该个数,数值相同自然个数相同。

我们假设原数值为A1 A2 A3 A4....  得到的个数加一的值为 R1 R2 R3 R4.....

既然比较范围是当前数值An和全部数值{A1~Amax}比较,

那么不难发现,唯一决定 Rn值的是An的值,与An的位置无关,An的位置决定的是Rn的位置。

所以1 6 2 2 7 --> 1 3 2 2 4 左边两个2的位置不同,但是得到右边的数值是一样的,也只是位置不同,假设有 1  2  5  2  3  2 那么得到的就是 1  2  4  2  3  2 。  

那看清这个规律之后,我们如果将原数组排序的话就会得到以下规律

1  2  2  6  7  --> 1  2  2  3  4 得到的R数组自然也是排好序的,如果说本来数组中每个值是随机的,从而到时比较后得到的数值也是随机的,那么原数组经过排序后再进行比较得到的有序差一递增数组就是解题的关键。

分析A数组排序后比较得到的R数组,发现是差值为一递增的,如果前后两个值相等没增,那就说明对应的原数组A中的两个数值是相同的,

有了这个规律,此题便破了。

当然,此时如果你听的一脸懵,为什么这样就算解出来了,那请看下我接下来的步骤。

1,把数组换成每个元素可以存储三个变量的结构体数组

struct node { long long num,long long id,long long ls;}  //数组中每个元素的结构

成员num存储原本用户输入的数值,

成员id存储的是每个值用户初始输入的顺序编号,

成员ls存储的是经过处理后得到的离散数值。

2,按照成员num的大小,将结构体A数组有序排列(递增),

3,将排序后数组中第一个元素的成员ls值赋值为h,(假设h初始化=1)

并且以此类推,如果下一个个元素不等于前一个元素,

那么就赋值下一个元素的成员ls为++h,如果前后两个元素相等,则直接赋值ls为h。

4,再将结构体数组A按照成员id的大小排序,恢复用户初始输入的顺序,然后遍历输出ls的值。

代码实现

结构体

struct node {
    long long num;
    long long id;
    long long ls;
};

对数值存储采用动态内存分配(满足内存的精确使用)

struct node **p = (struct node **)malloc((t + 1) * sizeof(struct node *));

    // 读取每组数据的数量
    for (int i = 1; i <= t; i++) {
        scanf("%d", &st[i]);
        p[i] = (struct node *)malloc((st[i] + 1) * sizeof(struct node));
        for (int j = 1; j <= st[i]; j++) {
            scanf("%lld", &p[i][j].num);
            p[i][j].id = j;
        }
    }

按照成员num排序  和 按照成员id排序的自定义函数

其中前面两个是利于读者理解的冒泡排序,但实际使用应使用后两个(快速排序)排序

以此来满足该题的时间问题。

// 按 num 字段排序
void num_paixu(struct node *a, int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n - i; j++) {
            if (a[j].num > a[j + 1].num) {
                struct node temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}

// 按 id 字段排序
void id_paixu(struct node *a, int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n - i; j++) {
            if (a[j].id > a[j + 1].id) {
                struct node temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}


/ 按 num 字段比较的函数,用于 qsort
int compare_num(const void *a, const void *b) {
    struct node *node_a = (struct node *)a;
    struct node *node_b = (struct node *)b;
    if (node_a->num < node_b->num) {
        return -1;
    } else if (node_a->num > node_b->num) {
        return 1;
    }
    return 0;
}

// 按 id 字段比较的函数,用于 qsort
int compare_id(const void *a, const void *b) {
    struct node *node_a = (struct node *)a;
    struct node *node_b = (struct node *)b;
    if (node_a->id < node_b->id) {
        return -1;
    } else if (node_a->id > node_b->id) {
        return 1;
    }
    return 0;
}

核心计算成员ls值的代码块

// 计算排名
    for (int i = 1; i <= t; i++) {
        long long h = 1;
        for (int j = 1; j <= st[i]; j++) {
            if (j == 1) {
                p[i][j].ls = h;
            } else {
                if (p[i][j].num == p[i][j - 1].num) {
                    p[i][j].ls = h;
                } else {
                    h++;
                    p[i][j].ls = h;
                }
            }
        }

代码汇总

#include <stdio.h>
#include <stdlib.h>

struct node {
    long long num;
    long long id;
    long long ls;
};

// 按 num 字段比较的函数,用于 qsort
int compare_num(const void *a, const void *b) {
    struct node *node_a = (struct node *)a;
    struct node *node_b = (struct node *)b;
    if (node_a->num < node_b->num) {
        return -1;
    } else if (node_a->num > node_b->num) {
        return 1;
    }
    return 0;
}

// 按 id 字段比较的函数,用于 qsort
int compare_id(const void *a, const void *b) {
    struct node *node_a = (struct node *)a;
    struct node *node_b = (struct node *)b;
    if (node_a->id < node_b->id) {
        return -1;
    } else if (node_a->id > node_b->id) {
        return 1;
    }
    return 0;
}

int main() {
    int t;
    scanf("%d", &t);
    int *st = (int *)malloc((t + 1) * sizeof(int));
    struct node **p = (struct node **)malloc((t + 1) * sizeof(struct node *));

    // 读取每组数据的数量
    for (int i = 1; i <= t; i++) {
        scanf("%d", &st[i]);
        p[i] = (struct node *)malloc((st[i] + 1) * sizeof(struct node));
        for (int j = 1; j <= st[i]; j++) {
            scanf("%lld", &p[i][j].num);
            p[i][j].id = j;
        }
    }

    // 按 num 字段排序
    for (int i = 1; i <= t; i++) {
        qsort(p[i] + 1, st[i], sizeof(struct node), compare_num);
    }

    // 计算排名
    for (int i = 1; i <= t; i++) {
        long long h = 1;
        for (int j = 1; j <= st[i]; j++) {
            if (j == 1) {
                p[i][j].ls = h;
            } else {
                if (p[i][j].num == p[i][j - 1].num) {
                    p[i][j].ls = h;
                } else {
                    h++;
                    p[i][j].ls = h;
                }
            }
        }
    }

    // 按 id 字段排序
    for (int i = 1; i <= t; i++) {
        qsort(p[i] + 1, st[i], sizeof(struct node), compare_id);
    }

    // 输出结果
    for (int i = 1; i <= t; i++) {
        for (int j = 1; j <= st[i]; j++) {
            printf("%lld", p[i][j].ls);
            if (j != st[i]) {
                printf(" ");
            }
        }
        printf("\n");
    }

    // 释放动态分配的内存
    for (int i = 1; i <= t; i++) {
        free(p[i]);
    }
    free(p);
    free(st);

    return 0;
}    

如果认为代码不好理解,有个简化版本的,省去动态内存分配和时间排序,

可以实现本题的全部功能,并且思路一致,但是在时间和内存的要求上,不满足本题。

可以尝试理解吸收掉这个简便的代码思路,然后使用动态内存分配和排序算法的优化来满足题意。

基础版本

#include <stdio.h>

struct node {
    long long num;
    long long id;
    long long ls;
}p[6][10001];//结构体

void num_paixu(struct node a[],int n) {
    for (int i = 1; i <= n; i++) 
    for (int j = 1; j <= n-i; j++) {
        if (a[j].num > a[j+1].num) {
            struct node temp = a[j];
            a[j] = a[j+1];
            a[j+1] = temp;
        }
    }
}   //按照成员num进行的排序

void id_paixu(struct node a[],int n) {
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n-i; j++) {
        if (a[j].id > a[j+1].id) {
            struct node temp = a[j];
            a[j] = a[j+1];
            a[j+1] = temp;
        } 
    }
    
}  //按照成员id进行的排序

int main() {
int t;
scanf("%d",&t);
int st[t+1];
for(int i=1;i<=t;i++)
{ scanf("%d",&st[i]);  //数组数量的赋值

for(int j=1;j<=st[i];j++)
{scanf("%lld",&p[i][j].num);//每个数组元素的赋值
p[i][j].id=j;
}
}

for(int i=1;i<=t;i++)
num_paixu(p[i],st[i]);  //排序
for(int i=1;i<=t;i++)
{
    long long h=1;
for(int j=1;j<=st[i];j++)  //按照既定规律计算成员ls的值
{   if(j==1){p[i][j].ls=h;}
else{
if(p[i][j].num==p[i][j-1].num){p[i][j].ls=h;}
else{h++;p[i][j].ls=h;}
}
}
}
for(int i=1;i<=t;i++)
id_paixu(p[i],st[i]);  //使用id排序恢复原顺序
for(int i=1;i<=t;i++)
{
for(int j=1;j<=st[i];j++){
printf("%lld",p[i][j].ls);  //输出ls值
if(j!=st[i])
printf(" ");
}
printf("\n");
}


    return 0;
}

原创思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LAOLONG-C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值