202006-2稀疏向量
題目
題目截圖:
![image-20211201234641418](C:\Users\long yongcheng\AppData\Roaming\Typora\typora-user-images\image-20211201234641418.png)
思路
60分答案
我的第一思路是開一個二維陣列,相當於開了一個兩行好多列的二維表(為什麼說好多列呢?因為主要取決於你那個只有多大),然後陣列下標就表示矩陣下標,然後將他們對應下標的值相乘再相加就能夠實現題目要求的內容了。
程式碼
/* 稀疏向量 */ #include <bits/stdc++.h> #include <iostream> using namespace std; const int N = 1e8 + 10; int arr[2][N]; int n, a, b; int main() { int index, value; cin >> n >> a >> b; for (int i = 0; i < a; i++) { cin >> index >> value; arr[0][index]= value; } for (int i = 0; i < b; i++) { cin >> index >> value; arr[1][index] = value; } int res = 0; for (int i = 0; i < n; i++) { res =arr[0][i] * arr[1][i]+res; } cout << res; return 0; } /* 10 3 4 4 5 7 -3 10 1 1 10 4 20 5 30 7 40 */
但是,這個解法只能夠拿到60分為什麼呢?你看題目要求,他的n
的資料範圍是到1e9
的,但是我們並不能看那麼大的陣列,所以,對於題目後面四個測試樣例,我們就過不了了。所以我們就必須想新的解決辦法了。
雙指標演算法
廢話
很巧,就在不久前,我也剛剛看了acwing
yxc的基礎演算法課,也剛好看到了他講的雙指標演算法
,那時本來想寫一點筆記的,但是發現那個東西並不好寫,然後只能作罷了。在做這道題的時候,我也想到了雙指標演算法,但是我寫的東西還是錯的,至於為甚麼,後面再講,還有,我在寫題目時還想到了用離散化
來做,但自己也是沒有寫出來。算了,廢話就不說了,下面開始講解:
正文
我們可以這樣子想:對於這兩個向量u
v
,我們可以用一個結構體來存下它們的有用值的下標(index
)以及它的值(value
),然後找到這兩個向量中下標相等的點,把他們的值相乘再相加就能夠得到兩個向量的內積了。思路是這樣子的,應該也不難理解。然後下面講講雙指標演算法的優勢是什麼,講完之後你們對雙指標應該也會有一個瞭解了。
傳統處理
按照我們一貫的思路,我們要找到兩個向量中下標相等的值,那麼我們可以把一個向量的某一個值暫時確定下來先,也就是作為第一重迴圈,然後在另一個向量中遍歷尋找和該值對應的下標,這是第二重迴圈。然後我們來分析一下時間複雜度:
兩個向量的維度最大可以達到5e5
,那麼時間複雜度就是\(O(n^2)=(5e5)^2=25*e^{10}\)
1e8
我們題目要求的時間是2秒,也就是說,我們這麼處理資料的話花費的時間是遠遠超過兩秒的,那交上去必然會導致超時。那麼這時候就能體現出雙指標演算法的優越之處了。
雙指標演算法
雙指標演算法怎麼處理資料的呢?
我們分析傳統處理方法,我們會發現對於每一個u
向量中確定下來的值,我們都要把v
中的每一個值無腦地拿出來進行比對。如果我對兩個向量中的index
從小到大排個序,如果當前u
向量的下標(設為i
)比v
向量下標(設為j
)大,那這時我應該要將j
移到和i
相等的位置才能做出比較,這樣我就可以不用理會中間的那些值,也就是說,我的j
值是跟這i
的值走的。這樣就可以讓i
和j
只遍歷一遍陣列就可以了,這樣就能夠把傳統的\(O(n^2)\)的演算法優化成\(O(n)\)的了。如果大家不能夠理解我上面的描述的話那就琢磨一下程式碼吧,如果程式碼也看不懂的話,那就等我有時間了在來寫一寫什麼是雙指標演算法吧。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
pair<int, int> u[N], v[N]; //此處pair可以看成是一個結構體,裡面有兩個元素。但是它和自己定義的結構又有不同,具體百度
int n, a, b;
int main() {
cin >> n >> a >> b;
for (int i = 0; i < a; i++) {
cin >> u[i].first >> u[i].second;
}
for (int i = 0; i < b; i++) {
cin >> v[i].first >> v[i].second;
}
sort(u, u + a );
sort(v, v + b );
long long res = 0;
for (int i = 0, j = 0; i < a && j < b; i++) {
while (j < b && v[j].first < u[i].first)j++;
if (j >= b)break;
if (u[i].first == v[j].first)res += u[i].second * v[j].second;
}
cout << res;
return 0;
}
離散化的做法
宣告:此處的程式碼不是本人寫的,但是我找不到原作者的部落格地址了,等找到再加上,如侵刪。
/* 稀疏向量 */
#include <iostream>
#include <map>
using namespace std;
map<int, int> mp;
int main() {
int n, m1, m2;
long long ans = 0;
cin >> n >> m1 >> m2;
for (int i = 0; i < m1; i++) {
int num1, num2;
cin >> num1 >> num2;
mp[num1] = num2;
}
for (int i = 0; i < m2; i++) {
int num1, num2;
cin >> num1 >> num2;
ans += mp[num1] * num2;
}
cout << ans;
}
/*
10 3 4
4 5
7 -3
10 1
1 10
4 20
5 30
7 40
*/
小結
- 雙指標演算法可以優化傳統的雙重遍歷
- 注意
pair
和自己定義的結構體的區別 - 學習使用一下map來實現離散化,這樣不用自己寫離散化函式
最後的話
這個題其實不算難吧(寫出來後看),但是自己在寫的過程中總是不能順利地實現自己的想法,雙指標和離散化我都有嘗試過,但還是不能寫出來,本來想把自己踩到的坑寫一下的,但是沒有時間了(寫部落格真的很費時間),等我以後休閒一點了在看著上來吧。其實我也已經把坑寫到小結那裡了,大家查查資料就能夠學到了。好吧,那今天就到這裡,拜拜啦!