題目:四平方和

看到這個題目,第一個思路就是:
枚舉abcd的值,然后判斷它們的平方和是不是等於N。
我們可以分析一下abcd的枚舉范圍:
a : 0 ~ sqrt( 5000000 / 4 )
b : 0 ~ sqrt( 5000000 / 3 )
c : 0 ~ sqrt( 5000000 / 2 )
d : 0 ~ sqrt( 5000000 )
這樣abcd的需要枚舉的范圍大約都是1000~2000,總枚舉量是1012這個量級。而108這個量級的計算量大約用時1秒。所以1012這個計算量肯定會超時。
超時我們就要想辦法優化。最先能想到一個優化的辦法——減少枚舉的變量。
思路2:
枚舉a、b、c,然后判斷 N - a2 - b2 - c2 是不是完全平方數。
同理,先分析一下abc的枚舉范圍:
a : 0 ~ sqrt( 5000000 / 4 )
b : 0 ~ sqrt( 5000000 / 3 )
c : 0 ~ sqrt( 5000000 / 2 )
可以發現abc的枚舉范圍仍然是1000~2000之間,總的枚舉量大概在109量級。再加上判斷一個整數X是不是完全平方數(開根號取整得到Y,然后再判斷Y*Y是不是等於X即可)復雜度是O(1)。所以整個算法的復雜度是O(N1.5),還是會超時。
還超時,說明我們還要繼續優化。
思路3:
只枚舉a、b,把余下的部分記作R,即 R = N - a2 - b2
於是我們的問題變成:c2 + d2 = R 有沒有解?如果有解,其中c最小的解是什么?由於題目要求是abcd聯合主鍵升序第一的解。所以這里要快速求出來c最小的解。比如這時R = 25,那么得到的解應該是 c = 0, d = 5,而不是 c = 3,d = 4。這里哈希表就派上用場了。我們可以預先求出來 R = c2 + d2 的解,用 unordered_map<int, int> f 來保存一個R對應的c。比如f[5]=1,表示R=5的解是 c = 1,(d=2可以由R和c算出來)。我們可以求出f[0], f[1], … f[5000000]的值,通過查哈希表用O(1)復雜度找到R = c2 + d2 的解,這樣就更進一步優化了。
代碼如下:
1 #include <cmath> 2 #include <iostream> 3 #include <unordered_map> 4 using namespace std; 5 6 int main() 7 { 8 int n; 9 unordered_map<int, int> f; 10 11 cin >> n; 12 //預處理,求取 R = c2 + d2 的解並保存在unordered_map 13 for(int c = 0; c * c * 2 <= n; c ++ ) 14 for(int d = c; c * c + d * d <= n; d ++) 15 if(f.find( c * c + d * d ) == f.end()) //在f中不存在則保存 16 f[c * c + d * d] = c; 17 18 for(int a = 0; a * a * 4 <= n; a ++) 19 for(int b = a; a * a + b * b <= n / 2; a ++) 20 if(f.find(n - a * a - b * b) != f.end()) //查哈希表,有解的話,計算並輸出 21 { 22 int c = f[n - a * a - b * b]; 23 int d = int(sqrt(n - a * a - b * b - c * c) + 1e-3); 24 cout << a << ' ' << b << ' ' << c << ' ' << d << endl; 25 return 0; 26 } 27 28 return 0; 29 }
