1. 程式人生 > 其它 >12屆藍橋杯省賽-砝碼稱重

12屆藍橋杯省賽-砝碼稱重

前言:

  在這一篇文章中,我們主要介紹“砝碼稱重”這道題的解法

正文:

一、題幹:

【問題描述】

  你有一架天平和 N 個砝碼,這 N 個砝碼重量依次是 W1,W2,...,WN。

  請你計算一共可以稱出多少種不同的正整數重量?

  注意砝碼可以放在天平兩邊。

【輸入格式】

  輸入的第一行包含一個整數 N。

  第二行包含 N 個整數:W1,W2,W3,...,WN。

【輸出格式】

  輸出一個整數代表答案。

【樣例輸入】

  3

  1 4 6

【輸出樣例】

  10

【樣例說明】

  能稱出的 10 種重量是:1、2、3、4、5、6、7、9、10、11。

  1 = 1;

  2 = 6 − 4 (天平一邊放 6,另一邊放 4);

  3 = 4 − 1;

  4 = 4;

  5 = 6 − 1;

  6 = 6;

  7 = 1 + 6;

  9 = 4 + 6 − 1;

  10 = 4 + 6;

  11 = 1 + 4 + 6。

【評測用例規模與約定】

  對於 50% 的評測用例,1≤N≤15。

  對於所有評測用例,1≤N≤100,N 個砝碼總重不超過 100000。

 

二、思路:

  這道題中,可以發現放入第一個砝碼的時候,只能稱出一個值,放入第二個砝碼的時候,可以只放第二個砝碼稱出一個值,可以把第一個放左邊第二個放右邊稱出一個值,可以把第一個放右邊邊第二個放左邊又稱出一個值。而第三個砝碼出現的時候,我們可以根據之前得到的值加上砝碼來得到新的值。總而言之,就是動態規劃。

  這樣題目就化簡為了如何用舊的值推出新的值,我們用一個一維陣列來儲存可以表示出的值,對每一個新的值引入的時候,我們做以下操作來推出新的值(設舊的值為j,新的值為a,陣列名為b)

  b[j+a]=1;(將j和a看作砝碼的話,就是j和a都放右邊(左物右碼))

  b[j-a]=1;(將j和a看作砝碼的話,就是j放右邊a放左邊)

  b[a-j]=1;(將j和a看作砝碼的話,就是j放左邊a放右邊)

  只不過這樣子會放出負數,而資料範圍說了砝碼總重不超過100000,所以最後操作的時候我們要把所有值都加100000防止訪問到負數。

 

  以題中所給的樣例為例進行演算:

  放入一個砝碼重量為1時(w為可以稱出的重量):

  w=1;

  放入二個砝碼重量為1,4時:

  w=1+4=5;(用w=1來求)

  w=1-4=-3;

  w=4-1=3;

  w=4;

  放入三個砝碼重量為1,4,6時:

  w=1+6=7;(用w=1來求)

  w=1-6=-5;

  w=6-1=5;

  w=5+6=11;(用w=1+4=5來求)

  w=5-6=-1;

  w=6-5=1;

  w=-3+6=3;(用w=1-4=3來求)

  w=-3-6=-9;

  w=6-(-3)=9;

  w=3+6=9;(用w=4-1=3來求)

  w=3-6=-3;

  w=6-3=3;

  w=4+6=10;(用w=4來求)

  w=4-6=-2;

  w=6-4=2;

  w=6;

  上面所有不重複的正數(也就是標藍的)數量就是答案:10。

  思路已經明瞭,所以程式呼之欲出:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int main(int argc, char const *argv[])
 5 {
 6     int b[200001];//是否被稱出,以及是前幾個稱出的
 7     memset(b,0,sizeof(b));
 8     int n;
 9     cin>>n;
10     int a;
11     for (int i = 1; i <= n; i++)
12     {
13         cin>>a;
14         for (int j = 0; j <= 200000; j++)
15         {
16             if(b[j]!=i&&b[j]!=0)
17             {
18                 if(b[j+a] == 0)b[j+a]=i;
19                 if(b[j-a] == 0)b[j-a]=i;
20                 if(b[200000+a-j] == 0)b[200000+a-j]=i;
21             }
22         }
23         b[a+100000] = i;
24     }
25     int sum = 0;
26     for (int i = 100001; i <= 200000; i++)
27     {
28         if(b[i]!=0)
29         {
30             sum++;
31         }
32     }
33     cout<<sum;
34 
35     return 0;
36 }

其中

 1     for (int i = 1; i <= n; i++)
 2     {
 3         cin>>a;
 4         for (int j = 0; j <= 200000; j++)
 5         {
 6             if(b[j]!=i&&b[j]!=0)
 7             {
 8                 if(b[j+a] == 0)b[j+a]=i;
 9                 if(b[j-a] == 0)b[j-a]=i;
10                 if(b[200000+a-j] == 0)b[200000+a-j]=i;
11             }
12         }
13         b[a+100000] = i;
14     }

  這一部分就是核心部分,說明一下,b不像前面樣例中存的是0和1,在這個程式中存的是整型變數,用來表示它可以由前b[j]個砝碼稱出,而b[j]!=i是為了防止重複使用一個砝碼,舉個例子:

  b[4+6]=3;

  b[4+6+6]=3;

  b[4+6+6+6]=3;

  如此就可以在使用i個砝碼的時候,只使用前i-1個砝碼了。

  另外,如果稱出0的重量算數嗎?(比如左邊放5,右邊放2+3)答案是不算哈~,所以在求結果的時候要把0給排除掉。

 

參考部落格:

  https://blog.csdn.net/asbbv/article/details/117253522

 

後記:

  感謝各位讀到這,雖然是AC了但是這道題絕對有更簡潔更快的方法,果然還是太菜了唉QAQ