1. 程式人生 > >51nod 1837 砝碼稱重【數學,規律】

51nod 1837 砝碼稱重【數學,規律】

可能 blank sed gif span nbsp sin pro n-1

題目鏈接:51nod 1837 砝碼稱重

小 Q 有 n 個砝碼,它們的質量分別為 1 克、 2 克、……、 n 克。

他給 i 克的砝碼標上了編號 i (i = 1, 2, ..., n),但是編號被人打亂了,即編號為 i 的砝碼不一定是 i 克,而是 a_i 克,這裏 a 指的是 1 到 n 的一個排列。 他有一桿天平,可以向天平的兩側放任意數量的砝碼,通過一次稱量得到兩側質量的大小關系,關系只有左側重、一樣重、右側重三種可能。 他想知道,最壞情況下,他至少需要稱量多少次,才能確定其中至少一個編號為 i 的砝碼的質量是 i 克或不是 i 克。 提示:
這裏所謂的最壞情況是指,對於固定的、按順序進行的稱量操作,不論每次稱量的結果是什麽,都能完成所需完成的上述判定任務。 例如 n = 6 時,可以只稱量一次,選擇編號為 1、 2、 3 的砝碼放在左側,編號為 6 的砝碼放在右側。 如果天平不是平的,則可以確定存在至少一個砝碼 i 不是 i 克 (i = 1, 2, 3, 6),否則編號為 6 的砝碼一定是 6 克。 再例如 n = 5 時,可以只稱量兩次,第一次選擇編號為 2、3 的砝碼放在左側,編號為 5 的砝碼放在右側,第二次選擇編號為 1、4 的砝碼放在左側,編號為 5 的砝碼放在右側。 這裏略去這樣稱量的正確性,留給做題人推導和證明。 Input
輸入包含多組測試數據。
每行對應一組測試數據,包含一個正整數 n 。
不超過 10^5 組數據,1 ≤ n ≤ 10^9。
Output
每行對應一組測試數據,輸出一個正整數表示答案。
Input示例
1
5
6
Output示例
0
2
1

題解:最先猜到1,3,6,10,15這些數比較特殊,都只要稱一次,因為:

3=1+2,6=1+2+3,10=1+2+3+4......

然後猜想其他數應該和其組合有關,比如5=1+4和2+3,然後4裏面有1+2和1+3(事實上只要124,判斷1+2<4就行了),然後猜測要稱的次數和最大砝碼的加法組合有關(必須有一邊要放一個,因為題目說要確定哪個錯了,唔其實這裏開始就想錯了)。。然後,沒猜出來。。。其中還猜想過答案只有1和2吧,但是我不會證明吖QAQ,後面越算越糊塗,棄療。。。

不扯了,,,花個點頭盾,

來看正解:(答案就是只有1和2哎。。。高斯證明過任意一個正整數可以表示成三個三角形數的和)

①如上猜想,1,3,6,10這樣的三角形數k*(k+1)/2,都只要一次,選出k個數之和判斷是否與n相等即可,因為任選k個數組成的最小質量和是n。

②n 等於 三角形數+1的也只要一次,與上同理,判斷選出k個數是否<n即可

③當第n個三角形數是平方數時,只要一次,判斷1+2+...+(k-1)=(k+1)+(k+2)+...+n是否成立,因為去掉一個砝碼後能夠拆分成兩個質量和相同的砝碼區間只有一種方案。

當第n個三角形數是平方數+1時,只要一次,與上同理。

⑤其他情況,n,n-1,n-2中至少有一個數可以表示成兩個三角形數的和,從而只需要稱兩次,因為小於號可以使用至多兩次。

(判斷一個數是不是平方數只需要將其開根下取整再平方進行檢驗,判斷三角形數同理。)

技術分享
 1 #include<cstdio>
 2 #include<cmath>
 3 using namespace std;
 4 typedef long long ll;
 5 ll n, t, k, kk;
 6 int main() {
 7     while(~scanf("%lld", &n)){
 8         if(n==1) {puts("0");continue;}
 9         k = sqrt(2*n-1);
10         t = n*(n+1)/2; kk = sqrt(t);
11         if(k*(k+1)/2 == n || k*(k+1)/2 + 1 == n ||
12            t == kk*kk || t == kk*kk+1) puts("1");
13         else puts("2");
14     }
15     return 0;
16 }
234ms

51nod 1837 砝碼稱重【數學,規律】