- 題目 - 開燈
- 題目描述
- 輸入格式
- 輸出格式
- 樣例 #1
- 樣例輸入 #1
- 樣例輸出 #1
- 提示
- AC CODE
- 思路
- AC CODE
- C++
- 說明
- 使用 Map 做資料標記,會出現 TEL
- 向下取整
- <math.h> 中常用的函式
題目 - 開燈
題目描述
在一條無限長的路上,有一排無限長的路燈,編號為 1 , 2 , 3 , 4 , ······。
每一盞燈只有兩種可能的狀態,開或者關。如果按一下某一盞燈的開關,那麼這盞燈的狀態將發生改變。如果原來是開,將變成關。如果原來是關,將變成開。
在剛開始的時候,所有的燈都是關的。小明每次可以進行如下的操作:
指定兩個數, a , t ( a 為實數, t 為正整數)。將編號為 ⌊a⌋ , ⌊2×a⌋ , ⌊3×a⌋ , … , ⌊t×a⌋ 的燈的開關各按一次。其中 ⌊k⌋ 表示實數 k 的整數部分。
在小明進行了 n 次操作後,小明突然發現,這個時候只有一盞燈是開的,小明很想知道這盞燈的編號,可是這盞燈離小明太遠了,小明看不清編號是多少。
幸好,小明還記得之前的 n 次操作。於是小明找到了你,你能幫他計算出這盞開著的燈的編號嗎?
輸入格式
第一行一個正整數 n ,表示 n 次操作。
接下來有 n 行,每行兩個數, ai , ti 。其中 ai 是實數,小數點後一定有 6 位, ti 是正整數。
輸出格式
僅一個正整數,那盞開著的燈的編號。
樣例 #1
樣例輸入 #1
3
1.618034 13
2.618034 7
1.000000 21
樣例輸出 #1
20
提示
記 T 為對 ti 求和的值,
- 對於 30% 的資料,滿足 T < 1000 ;
- 對於 80% 的資料,滿足 T < 200000 ;
- 對於 100% 的資料,滿足 T < 2000000 ;
- 對於 100% 的資料,滿足 n < 5000 , 1 < ai <1000 , 1 < ti < T 。
資料保證,在經過 n 次操作後,有且只有一盞燈是開的,不必判錯。而且對於所有的 i 來說, ti * ai
的最大值不超過 2000000 。
AC CODE
思路
- 題目的資料量 > 2e6 ,使用陣列(array)是裝不下的,而且最後遍歷陣列的時間複雜度也非常高
- 因此總體思路是,使用某個結構,標記每次操作後出現變化的燈
使用 Map
- 每次進行 map.find 操作,如果該燈已被標記,則翻轉燈的狀態,如果該燈未被標記,則將其打上 "已被點亮" 的標記
- 遍歷 map 中的元素,其中標記為 "點亮" 的燈為結果
使用 Set
- 設定點亮的燈被放入 set 中,那麼每次對被選中的燈進行 set.find 操作,如果 find 成功,表示燈需要被熄滅,則進行 set.erase 操作,如果 find 失敗,則表示該燈需要被點亮,進行 set.insert 操作
- 最後 set 中只會剩下被點亮的燈,在進行結果遍歷時會更快
AC CODE
C++
#include <iostream>
#include <set>
using namespace std;
int main()
{
int t;
cin >> t;
set<int> opare_set;
while (t != 0)
{
double num;
int k;
cin >> num >> k;
for (int i = 1; i <= k; i++)
{
int temp = (int)(1.0 * num * i);
if (opare_set.find(temp) == opare_set.end())
{
opare_set.insert(temp);
}
else
{
opare_set.erase(temp);
}
}
t--;
}
for (auto it : opare_set) {
cout << it << endl;
}
return 0;
}
說明
使用 Map 做資料標記,會出現 TEL
-
因為 set 的查詢效率更高,它的增刪改查時間複雜度都為 logN
set 會對集合中的元素進行排序,set 的底層資料結構是紅黑樹(平衡二叉樹),因此查詢效率更高
-
最後 set 中只會剩下被點亮的燈,在進行結果遍歷時會更快
-
和 Java 中 Set 容器的底層實現類似,Set 是一組只用 key 值的 Map 結構
在此附上使用 Map 之後 TEL 的程式碼
#include<bits/stdc++.h>
using namespace std;
int lowerN(double n) // 向下取整函式
{
return floor(n);
}
int main()
{
int k;
cin>>k;
map <int,bool> flag; // map記錄被改變狀態的燈
for(int h=0;h<k;h++)
{
double a;
int t;
cin>>a>>t;
for(int i=1;i<=t;i++)
{
int temp=1.0*a*i;
temp=lowerN(temp);
//迭代器 記錄燈是否改變狀態
map <int,bool>::iterator iterflag;
iterflag=flag.find(temp);
if(iterflag==flag.end())
{
flag.insert(pair<int,bool>(temp,true));
}
else
{
flag[temp]=!flag[temp];
}
}
}
int anser;
map <int,bool>::iterator iteranser;
for(iteranser=flag.begin();iteranser!=flag.end();iteranser++)
{
if(iteranser->second==true)
{
anser=iteranser->first;
break;
}
}
cout<<anser<<endl;
return 0;
}
向下取整
- 在 C/C++/Java 中,使用
(int)
等進行強制型別轉換,計算機會採用丟失精度的方式進行強轉,得到的結果就是保留了數值的整數位 - 可以使用
<math.h>
標頭檔案中的 floor 函式完成取整操作
<math.h> 中常用的函式
參考自以下文章
math.h標頭檔案下的常用函式!取整,取絕對值,四捨五入......看這一篇就夠了!
math.h - 百度百科
取整函式
- floor(double x):向下取整函式,標準定義為取比 x 小的最大整數,因此
floor(-2.3) = -3
- ceil(double x):向上取整函式,標準定義為取比 x 大的最小整數,因此
ceil(-2.3) = -2
取絕對值
- abs(int x):對整型取絕對值
- fabs(double x):對浮點型取絕對值
- labs(long x):對長整型取絕對值
四捨五入
- round(double x):四捨五入函式
運算
- pow(double x, double y):冪函式,求 x 的 y 次方,當 y = 0.5,就是對 x 求平方根
- log(double x):對數函式,求以 e 為底數 x 的對數
- sqrt(double x):對 x 開平方函式,得到一個大於 0 的結果