1. 程式人生 > 其它 >202006-2稀疏向量

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的,但是我們並不能看那麼大的陣列,所以,對於題目後面四個測試樣例,我們就過不了了。所以我們就必須想新的解決辦法了。

雙指標演算法

廢話

很巧,就在不久前,我也剛剛看了acwingyxc的基礎演算法課,也剛好看到了他講的雙指標演算法,那時本來想寫一點筆記的,但是發現那個東西並不好寫,然後只能作罷了。在做這道題的時候,我也想到了雙指標演算法,但是我寫的東西還是錯的,至於為甚麼,後面再講,還有,我在寫題目時還想到了用離散化來做,但自己也是沒有寫出來。算了,廢話就不說了,下面開始講解:

正文

我們可以這樣子想:對於這兩個向量u

v,我們可以用一個結構體來存下它們的有用值的下標(index)以及它的值(value),然後找到這兩個向量中下標相等的點,把他們的值相乘再相加就能夠得到兩個向量的內積了。思路是這樣子的,應該也不難理解。然後下面講講雙指標演算法的優勢是什麼,講完之後你們對雙指標應該也會有一個瞭解了。

傳統處理

按照我們一貫的思路,我們要找到兩個向量中下標相等的值,那麼我們可以把一個向量的某一個值暫時確定下來先,也就是作為第一重迴圈,然後在另一個向量中遍歷尋找和該值對應的下標,這是第二重迴圈。然後我們來分析一下時間複雜度:

兩個向量的維度最大可以達到5e5,那麼時間複雜度就是\(O(n^2)=(5e5)^2=25*e^{10}\)

,我們大概知道計算機1s能夠處理的資料大概是1e8 我們題目要求的時間是2秒,也就是說,我們這麼處理資料的話花費的時間是遠遠超過兩秒的,那交上去必然會導致超時。那麼這時候就能體現出雙指標演算法的優越之處了。

雙指標演算法

雙指標演算法怎麼處理資料的呢?

我們分析傳統處理方法,我們會發現對於每一個u向量中確定下來的值,我們都要把v中的每一個值無腦地拿出來進行比對。如果我對兩個向量中的index從小到大排個序,如果當前u向量的下標(設為i)比v向量下標(設為j)大,那這時我應該要將j移到和i相等的位置才能做出比較,這樣我就可以不用理會中間的那些值,也就是說,我的j值是跟這i的值走的。這樣就可以讓ij只遍歷一遍陣列就可以了,這樣就能夠把傳統的\(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

 */

小結

  1. 雙指標演算法可以優化傳統的雙重遍歷
  2. 注意pair和自己定義的結構體的區別
  3. 學習使用一下map來實現離散化,這樣不用自己寫離散化函式

最後的話

這個題其實不算難吧(寫出來後看),但是自己在寫的過程中總是不能順利地實現自己的想法,雙指標和離散化我都有嘗試過,但還是不能寫出來,本來想把自己踩到的坑寫一下的,但是沒有時間了(寫部落格真的很費時間),等我以後休閒一點了在看著上來吧。其實我也已經把坑寫到小結那裡了,大家查查資料就能夠學到了。好吧,那今天就到這裡,拜拜啦!