離散化的思想和它的兩種程式碼與區別
離散化是什麼:一些數字,他們的範圍很大(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是離散化後的數字;