1. 程式人生 > >2016第七屆藍橋杯C++B組第八題:四平方和

2016第七屆藍橋杯C++B組第八題:四平方和

題目:


四平方和

四平方和定理,又稱為拉格朗日定理:
每個正整數都可以表示為至多4個正整數的平方和。
如果把0包括進去,就正好可以表示為4個數的平方和。

比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符號表示乘方的意思)

對於一個給定的正整數,可能存在多種平方和的表示法。
要求你對4個數排序:
0 <= a <= b <= c <= d
並對所有的可能表示法按 a,b,c,d 為聯合主鍵升序排列,最後輸出第一個表示法


程式輸入為一個正整數N (N<5000000)
要求輸出4個非負整數,按從小到大排序,中間用空格分開

例如,輸入:
5
則程式應該輸出: 0 0 1 2 再例如,輸入: 12 則程式應該輸出: 0 2 2 2 再例如,輸入: 773535 則程式應該輸出: 1 1 267 838 資源約定: 峰值記憶體消耗 < 256M CPU消耗 < 3000ms 請嚴格按要求輸出,不要畫蛇添足地列印類似:“請您輸入...” 的多餘內容。 所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。 注意: main函式需要返回0 注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。 注意: 所有依賴的函式必須明確地在原始檔中 #include <xxx>, 不能通過工程設定而省略常用標頭檔案。
提交時,注意選擇所期望的編譯器型別。

思路:用陣列sum[i]存i的平方(5000000的1/2次方為2236,所以陣列只需開2237即可),先找到比輸入資料x大的前一個位置xi, 遍歷f(xi)到f(1)(f函式從xi開始向前貪心獲取比當前x小的數的1/2次方,得到一組合法的an),將an轉化為對應字串放入set中自動升序排序,最後輸入set中的第一個元素。

程式碼:

// 四平方和
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll N = 2237;

ll sum[N+1]; // √5000000  = 2236+ 
ll an[4]={0,0,0,0}; // 返回平方大於x的前一個位置 int getpos(ll x) { for(int i=0;i<=N;++i) { if(sum[i]>=x) return i-1; } return -1; } // 將陣列轉化為相應字元組成的字串, 字元間用空格隔開 string a2s() { ostringstream ostr; for(int i=0;i<4;++i) ostr << an[i] << " "; return ostr.str(); } // 根據xi提供一組an (合法則返回true) bool f(ll x,int xi) { memset(an,0,sizeof(int)*4); int i=3; while(i>=0&&x>0) { for(int j=xi;j>0;--j) { if(x==sum[j]) { an[i] = j; x=0; --i; break; } else if(x>sum[j]) { while(x>=sum[j]&&i>=0&&x>0) { an[i]=j; x-= sum[j]; --i; } } } } if(x>0) return false; return true; } set<string> sset; int main() { for(int i=0;i<=N;++i) sum[i] = i*i; ll x; cin >> x; int xi = getpos(x); while(xi>0) { if(f(x,xi)) sset.insert(a2s()); --xi; } // 第一個元素即是最終結果 set<string>::iterator it = sset.begin(); string res = *it; cout << *it << endl; return 0; }