seansie's blog

zerojudge b512 高維度稀疏向量 | seansie blog

題目

https://zerojudge.tw/ShowProblem?problemid=b512

輸入兩個向量,計算向量內積值。兩個向量的內積,是各項相乘然後加總。例如 [1,2,3] 和 [4,5,6] 內積是 14+25+3*6 = 32

我們考慮高維度的稀疏向量,也就是大多數的元素都是零,只有少數不為零。資料的表示方式如下 dim1: value1 dim2: value2 dim3:value3 … 0:0 最後以 0:0 結束。例如

向量 [0,5,0,0,9,0,0,33] 是一個 8 維向量,可表示成 2:5 5:9 8:33 0:0 值為 0 的維度都可以忽略不需描述,只需記錄非零的維度。利用上述的表示法,讀取兩個向量,然後算出它們的內積。

Input

輸入兩行,分別對應到兩個整數向量。向量維度最高不超過 2 的 31 次方。記憶體用量不超過 32 MB。每一行都是以 0:0 結束

Output

內積值 最後記得換行

破題

如題所示,這種稀疏向量如果照著正常的方式來儲存,不僅浪費空間而且沒有效率,因此會採用題目所提到的格式來儲存 dim1: value1

而該格式自然而然的會讓人想到鍵值對(有點類似電話簿,名稱對應電話號碼,在這個例子中是維度對應數值),而在 C++ 中的鍵值對就是 map

因此首先先宣告一個 map 物件

map<long long ,long long> m;

使用 long long 避免潛在的溢位問題

接下來,因為題目提到 **每一行都是以 0:0 結束 ,**所以要用個while迴圈持續接收資料,因此先宣告 dimval 變數,因為它在未初始化的前提下可能會等於 0 ,所以先給它強制初始化成 -1 ,避免潛在錯誤。而 prod 是用來儲存答案的變數,一樣先初始化成 0,因為從0開始。

	long long  dim=-1,val=-1,prod=0;

然後開始讀入 dimval ,對於使用 C++ 的人,建議使用 scanf 而不要使用 cin ,不然還要自己做字串處理,而藉由 scanf 的格式化輸入功能就能自動擷取出 dim1: value1

然後讀取成功後將他存入 m 物件

while(dim!=0 && val!=0) {
		scanf(" %lld:%lld",&dim,&val);
		m[dim]=val;
	}

當跳出這個迴圈後,代表已經讀取完第一個稀疏向量了,要開始讀取第二個了。

有以下幾種情況

  • 第二個向量的第i維度是 0
  • 第一個向量的第i維度是 0
  • 或是同時兩者成立
  • 兩者都不成立

第一個情況好處理,沒有在第二個向量獨到的維度忽略就好,順順讀就好。

第二種情況的話,可以轉換成,在剛剛 m 鍵值對物件中, 該維度是找不到對應的值得。

第三種情況轉換成最簡單的第一種情況就好。

第四種情況就把透過該維度為鍵所查詢到的值,跟剛剛獨到的 val 相乘,加上 prod 上就好

綜合以上情形,可以這樣處理

while (dim!=0 && val!=0){
		scanf(" %lld:%lld",&dim,&val);
		if(m.find(dim)!=m.end()){
			prod+=m[dim]*val;
		}
	}

其中 m.find(key) 會回傳 鍵 keym 物件所在的位置,重點來了,如果沒找到(找不到對應的鍵),會回傳 m.end() ,因此可以用這個判斷情況二的成立與否。

最後印出來就好

printf("%lld\n",prod);

完整C++程式碼

#include<bits/stdc++.h>
using namespace std;

int main(){
	map<long long ,long long> m;
	long long  dim=-1,val=-1,prod=0;
	while(dim!=0 && val!=0) {
		scanf(" %lld:%lld",&dim,&val);
		m[dim]=val;
	}
	dim=-1,val=-1;
	while (dim!=0 && val!=0){
		scanf(" %lld:%lld",&dim,&val);
		if(m.find(dim)!=m.end()){
			prod+=m[dim]*val;
		}
	}
	printf("%lld\n",prod);
}