1. 程式人生 > >離散化的思想和它的兩種程式碼與區別

離散化的思想和它的兩種程式碼與區別

離散化是什麼:一些數,他們的範圍很大(0-1e9),但是個數不算多(1-1e5),並且這些數本身的數大小不重要,重要的是這些數之間的相對大小(比如說某個數是這些數中的第幾小,而與這個數本身大小沒有關係,要的是相對大小)(6 8 9 4 離散化後即為 2 3 4 1)(要理解相對大小的意思)(6在這4個數中排第二小,那麼就把6離散化成2,與數6本身沒有關係, 8,9,4亦是如此)(2018.3.26 對這篇部落格進行補充修改,被一道題的離散化卡到了,花了一晚上時間,才找到BUG(需離散化的數有無相同的數),黑體為今晚對此篇部落格進行了補充完善與區別)

離散化思想:因為數字太大,導致沒有辦法開那麼大的陣列,又因為數字個數並不多,這時候就可以對它們進行離散化,離散化是改變了數字的相對大小,例如,有500000個數字,他們的範圍是0-1e9的,這樣就滿足離散化的條件。

就比如說,你可以開一個5e5的陣列,但是你不能開一個1e9的陣列。只改變這些數字的相對大小

第一種離散化

(包含重複元素,並且相同元素離散化後也要相同,推薦使用)
離散化以前一直搞不懂是怎麼實現的,看了一個程式碼才明白。

const int maxn=1e5+10;
int a[maxn], t[maxn], b[maxn];
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
    scanf("%d",a[i]),t[i]=a[i];
sort(t+1,t+n+1);
m=unique(t+1,t+1+n)-t-1;//求出的m為不重複的元素的個數
for(int i=1; i<=n; i++)
    b[i]=lower_bound(t+1,t+1+m,a[i])-t;
//a[i]為原來的陣列,b[i]為離散化後的陣列

原來的a[i]離散化後成了後來的a[i];

離散化後的a[i]範圍是(1-m);
舉個栗子:
原序列:6 9 4 6 4
排序後:4 4 6 6 9
unique(元素去掉重複的)後:4 6 9 6 9  ( 感謝薇亞040214同學提出疑問,為什麼unique去重後是4,6,9,6,9,而不是4,6,9,4,9,大家執行下面的程式碼即可,2018.7.21更)

#include <cstdio>
#include <algorithm>
using namespace std;
int a[10]={6,9,4,6,4};
int main()
{
    int n=5;
    sort(a,a+n);//排序後4 4 6 6 9
    n=unique(a,a+n)-a;
    for(int i=0;i<5;i++)
        printf("%d ",a[i]);
    printf("\n");
    //最後輸出4 6 9 6 9
    //SiriusNEO大佬的解答:unique去重完後面的元素是不變的,所以是4 6 9 6 9,具體可以看C++ Reference的原始碼
}


unique有一個返回值,例如有十個有序的數列3 3 5 5 6 6 6 7 7 8,不重複的數字有五個,使用unique去重之後數列變成了3 5 6 7 8 6 6 7 7 8,它只改變了前五個數字後邊的不變,返回值是 最後一個改變的數字的地址。so:m=unique(t+1,t+1+n)-t-1;一般要減去首地址(t+1),m為不重複的數字的個數

  第二種離散化

(複雜度低,1.包含重複元素,並且相同元素離散化後不相同,2.不包含重複元素,並且不同元素離散化後不同,符合這兩種的其中一個,推薦使用  |   感謝Angel-Yan同學指出錯誤,2018.7.21更正)

struct A
{
    int x, idx;
    bool operator < (const A &rhs) const
    {
        return x < rhs.x;
    }//也可以寫個cmp函式排序
};
A a[MAXN];
int rank[MAXN];
int n;
scanf("%d",&n);
for(int i = 1; i <= n; ++i)
{
    scanf("%d", &a[i].x);
    a[i].idx = i;
}
//for(int i=1; i<=n; i++)
//    printf("%d  %d\n",a[i].idx,a[i].x);
//printf("\n");
sort(a + 1, a + n + 1);
//for(int i=1; i<=n; i++)
//    printf("%d  %d\n",a[i].idx,a[i].x);
//printf("\n");
for(int i = 1; i <= n; ++i)
{
    rank[a[i].idx] = i;
//    printf("rank[%d] = %d\n",a[i].idx,i);
}

給你們個例子:
i      1 2 3 4
x     6 8 9 4
idx  1 2 3 4
排序後:

i      1 2 3 4  //離散化後的數
x     4 6 8 9 
idx  4 1 2 3  //數原來的所在的位置編號
將上面兩行黑體數字對應起來 即是:rank[4]=1,rank[1]=2,rank[2]=3,rank[3]=4;  //rank[i]=j表示將原來在第i個位置上的數字離散化成j
so:
rank[1]=2;
rank[2]=3;
rank[3]=4;
rank[4]=1;
so:   6 8 9 4

就離散化為2,3,4,1
如果你想用原來的數字,就用排過序的結構體a[2]=6,a[3]=8,a[4]=9,a[1]=4; //a[i]=j表示排過序後現在的a數組裡第i個數字的是j。j也就是第i小。
a[i]是原來的數字;
rank是離散化後的數字;