因為 +
加上 +
會變成 -
,而 -
的絕對值很小,代表影響很小,
因此只要 +
的數量是奇數的話,算出來的答案就會是正數,反之亦然,
所以只要計算 +
的數量就可以知道答案了。
我們無法從輸入得知 -
到底是 , 還是 ,
+
也是一樣情況,所以不管是哪一種,答案都應該相同,
因此我們可以把 +
當作 ,把 -
當作 ,
直接做計算,不過在 C++ 中,有號數溢位是 Undefined Behavior,
因此需先轉為無號數進行二進制加法,再將結果轉為有號數。
Unsigned integer arithmetic is always performed modulo where is the number of bits in that particular integer.
When signed integer arithmetic operation overflows (the result does not fit in the result type), the behavior is undefined.
而無號數加有號數,有號數會被隱性轉型為無號數。
If the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned operand's type.
從限制可以看到 ,代表可以分別定義 個變數分別紀錄 有幾個、 有幾個,以此類推。
輸出的時候只要先輸出這 個變數的值,然後再輸出 個 就可以通過 Subtask 1 了。
因為 以後的都不可能出現,所以後面的必定都為 。
現在 的最大值從 增加到 了,如果要延續 Subtask 1 的作法,
從 個變數增加到 個變數,且多增加許多 if
條件判斷,也是可以通過。
但是這個方法光是要一直複製貼上就很累人,因此可以用陣列來簡化那些繁瑣的程式碼。
宣告一個長度為 的陣列 arr
,而 arr
的每一個元素都代表其對應的箱子裡面有多少物品。
每次讀到一個 的時候就將對應的元素加 ,最後再將陣列輸出就可以拿到滿分了。
我的實作方式是將
arr[i]
對應到第 個箱子,
因為題目的箱子編號是從 開始,而陣列的索引值是從 開始。
一開始不曉得偉杰有幾顆石頭,於是假設偉杰目前擁有 顆石頭
因為在還沒做任何操作前,偉杰最少可以有 顆
而操作過程中若偉杰只有 顆石頭,但還是要拿走 顆石頭的話
則這個操作就是不符合規則的操作,需要假設偉杰一開始的石頭要再多一顆:
舉例來說,若操作為 --+
設偉杰一開始有 顆石頭,則目前有 顆石頭
下列 1、2、3 分別表示 --+
各個操作:
此子任務只要照著題目敘述實作即可得分。
請注意:這個子任務的變數範圍會超出 int 範圍,
請使用 long long 儲存數值。
假設
觀察一下會發現,大的數字重複減去小的數,
一直重複這個動作的話會有以下兩種可能:
若是 1. 的話則得到題目所需的答案
若是 2. 的話則將 對調並繼續執行相減的操作
若一開始 的話,則不會執行任何減法,
會直接導到 2. ,因此會對調 後繼續執行減法,
可以得知一開始 相對關係不影響此作法正確性。
你如果直接將此作法送上去會得到一個 TLE,
因為如果 分別為 與 ,
則該作法會花費很長的時間執行,
因此有一個方法可以加速相減的過程。
當你要重複執行 直到 時,
則該操作會等價於 對 取模,
也就是 的餘數,在 C++ 可以用 a % b
得到。
如此一來就可以在很短的時間內得到答案。
此做法即為輾轉相除法,
而輾轉相除法可以求出兩個數的最大公因數(GCD),
因此做出這題的各位其實已經會求兩數的最大公因數了喔。
對於每個人,檢查是否表現得比其他人都好,複雜度為 。
因為最多只有一個人有機會表現得比其他人都好,
可以先兩兩進行比較,淘汰掉一個人,再找下一個比較,
次後可淘汰掉 個人,剩下的一個人只需要再與全部人比較一次即可,
複雜度為 。
一個格子最多只會跟 個格子相鄰,因此數字最多為 ,
只要枚舉每個格子的數字(),
並檢驗相鄰的非 數量以及是否大於原本的數字即可,
如果枚舉所有可能都沒找到,則輸出 NO
。
枚舉複雜度 ,檢驗 ,總複雜度
根據上面所述,每個格子可分為下列三種情況:
只要一開始輸入的數字違反了上述的條件,則該筆輸入答案為 NO
,
否則只要將數字填為可能的最大值,每個格子都是非 ,
所以也剛好讓數字都可以達到最大值,範例如下:
這題主要是想藉著開學考讓大家知道有這種互動的題型,
希望寫完這題後可以大致上知道互動題的運作模式,
OJ 上面也還有其他互動題可以讓各位練習
只要有 Interactive 標籤的都是互動題。
另外,這題只要有認真讀完題敘就應該有 分可以拿,
鼓勵各位每一題都應該要仔細閱讀,
不要因為某一題過不了而一直卡在那邊。
直接把題目附的檔案上傳就可以了,因為 ,所以只有兩種情況:
你只要不斷的射一顆,直到對方投降或是輪到自己的時候剩一顆而投降
最終輸的條件是輪到自己時只剩 顆,可以藉由這個推導,
如果雙方都採取最佳策略的話,那麼當氣球數量為 顆時,你最多只能射 顆,
如此一來會剩下 顆,如果對方射掉一顆留下最後一顆,你就輸了;
若你只射 顆的話,對方可以選擇射 顆,
最後還是剩下最後一顆,一樣是你輸。
由於後手可以將每次雙方射掉的氣球數量控制在 ,
從上面這邊可以推論,當輪到某人時的氣球數量為 時,
該玩家在這場遊戲的狀態必輸。
因此如果開場的氣球數量除以 的餘數為 時則必輸,
否則只要每次射完後將氣球數量控制在 即可贏得比賽。
需要注意不要射到負數個氣球,在此用 mod_abs
取正的餘數
直接模擬題目敘述中的做法
當起點試到第 個格子時,作法如下:
該做法的複雜度為
假設 為硬幣移到 格子時,目前可得到的最大分數
例如 ,則
從起點 可跳到 共得到 分
若從起點 跳至 只能得 分
假設對於格子 ,已知比 小的格子只有 和 格子能一步到達
也就是說 及
若想知道 為多少,則只需看 和 何者較大
然而這裡的 及 也得靠比 或 小的格子求得
依此類推,若想算出 ,需要從第 格至 格依序計算
實作上,利用陣列 s
當起點選到第 格時,就更新 s[i+a[i]]
的值
這邊要注意到當選到 格時,
s[i]
早已得到答案
這樣在之後選到此 i+a[i]
格子時,就已經計算完到該格的最大分數了
以上該做法的複雜度為
在分成兩個子任務看之前,我們需要先分析題目要的東西。
每次操作都會得到 分,也就是說做了 次之後會獲得的分數是 。
代表第 次操作消去的石頭數量。
上面的公式可以化簡成 ,如果石頭被消完的話,可以發現 會等於石頭總數,也就是 。
因此最後獲得的分數可以寫成 。
從公式中可以看出,會影響最後總分大小的只有 這個變數,也就是消完全部石頭的總操作數。
在這個子任務中, 不會是負數,因此要得到最大分數的方法就是讓 愈大愈好。
讓 最大的方法就是每次操作都只消除一個石頭,所以這時 就會等於 。
答案為 。
現在多了 是負數的情況,因此當 是負數時必須要讓 愈小愈好。
這邊將連續的一段石頭視為一個「區段」,舉例來說,
WWW
就只有一個區段,而 WWWBBW
有 個,WBWBW
有 個。
消除全部的石頭就意味著要將所有的區段消除。
每次的操作都固定為從現有的區段中選出其中一個,並將這個區段中的所有石頭都消除。
只消除區段中的部分石頭只會多浪費一次操作,並不會讓結果更好。
假設現在有 個區段,要怎麼選才會得到最小的 ?
可以觀察一下,如果 ,消除第 個或是第 個區段之後會讓區段的總數少 。
但是如果選擇其它的區段的話,在消除之後左右兩邊的區段會合併成一個區段,所以會讓區段的總數少 。
因此最好的取法就是每次都選頭尾以外的區段來消除。
如果 ,那就只能花 次操作把所有區段消除。
這邊很直覺的就可以寫出以下程式來算出 ,
但其實 可以用一個公式就算出來,將 分成奇數和偶數兩種情況去分析:
上面兩個公式可以總結成 。
有了 之後就可以直接算出最後的總分了。
枚舉每個人最後所錄取的志願,並驗證是否符合抽籤規則,
如果枚舉過程中發現某堂課假設的錄取人數超過該堂課上限,
則不管後面的枚舉狀況如何,該枚舉必然不成立,
因此可以適當排除這些不可能的組合以加速枚舉過程,
這種技巧稱為「剪枝」
而驗證枚舉結果合法性的方式如下:
可以直接跑過所有人的第一志願,如果該志願是該學生錄取的志願,則將他錄取。
接著再跑一次第一志願看有沒有人有資格錄取卻沒被錄取。
如此一來志願較前面的人一定會優先被錄取到,
而且不會有明明前面的課程還有餘額卻沒有錄取到的狀況。
此驗證方法總複雜度為 ,
而枚舉的複雜度為 ,
因此算法總複雜度為 。
要特別注意的是,有可能會有人沒有錄取任何志願,因此要記得枚舉這種狀況。
可以觀察 Subtask 1 驗證的過程,就可以發現以下做法:
從每個人的第一志願開始看,如果餘額還有剩就將該學生錄取進該課程,
接下來再看還沒有錄取任何課程那些人的第二志願,
接著重複以上動作,就可以完成抽籤了,總複雜度