#49900: 這題的測資格式應該是有問題的,與題目敘述不符


sam851015@gmail.com (多挖鼻孔有益身心健康)

學校 : 臺中市立惠文高級中學
編號 : 277705
來源 : [123.192.228.253]
最後登入時間 :
2025-09-21 22:24:46

python 正常應該是過不了這題的,我猜啦,我用上了 sys.stdin ,用了一些莫名其妙的方法處理 MLE,面對 #4 那個超大的測資檔依然 TLE

真的想用 python 寫的人可以去 o067 玩,基本邏輯是相同的

若你計算邏輯正確,把你用在這題的程式碼直接複製貼上過去那題應該就能 AC 了

 

--

希望有權限修這題測資的人能處理,至少測資格式要對啊!!

 

以下正文

--

 

先說結論,測資格式是絕對有問題的

 

#3, #4, #5 缺少換行符,有好幾行資料都放在同一行

在這三筆測資中,一行可不只 2 個數字,而是好幾個數字都放同一行!

可以理解為極端情況下有 10^7 +2 個數字塞在同一行,這不爆記憶體才怪!

 

下面我會說明我怎麼證明格式有問題

據題目所述

第一行有兩個數字 n , m

接下來有 m 行

每行有兩個數字 a b

n < 10000

m < 10^7 

1 <= a

b <= n

 

意味著我們可以相信無論如何,每一行最多就兩個數字,這兩個數字間用空格隔開

且這兩個數字的大小除了第一行的 m 有可能大到 10^7 這麼大外,

其他的 n, a, b 絕對不會超過 10000

 

意即,第一行最多 5 + 1 + 8 = 14 個字元 (最大就這樣 -> "1000 10000000")

其他每一行最多 5 + 5 = 10 個字元 (最大就這樣 -> "10000 10000",當然這是不合法輸入,a 不應與 b 相等,不過暫且把這算在內了)

 

如果就只有這樣,那 python 絕對可以應付,絕對不會 MemoryError

有超長字串的題目在 zerojudge 很多,但只要 python 能開起來,不至於讀一行資料就爆記憶體給你看

這題最多 14 個字元,沒道理一讀進來直接爆

所以要考慮的就是 IO 優化,改善讀取效率

於是我送出了我的第一版程式 (想抄就抄吧,反正不會 AC)

 

from sys import stdin

scan = stdin.readline

counter = {}
for line in stdin:
  if not line.strip():
      print()
        continue

  n, m = map(int, line.strip().split())
  for _ in range(m):
      a, b = map(int, scan().strip().split())
      counter[a] = counter.get(a, False) ^ True
        counter[b] = counter.get(b, False) ^ True

  odd_cnt = sum(counter.values())
  print("YES" if odd_cnt in (0, 2) else "NO")
    counter.clear()

 

結果是 NA (score:91%)

在 #4, #5, #6 這三個測資檔特別大的題目中都 MLE 了,應該也是大部分用 python 的人遇到的問題

就很怪異,對吧,我想過 TLE,但就是沒有想過 MLE

細讀了 RE 的資訊後,我發現出現 MLE 的位置還有不同

 

#4 和 #5 的 MLE 發生在讀取各節點相鄰資料的階段

#6 則是發生在第一行,才讀第一行就 MLE 了

 

就很離譜

如果就 2 個 5 位數字,第一行了不起多一個 8 位數,那也不可能這樣就爆記憶體了

 

思索解決方案後,我決定遇到 MLE 就瞎猜答案

於是我直接 try 捕捉 MemoryError,然後隨便亂填東西進去

from sys import stdin

scan = stdin.readline

counter = {}
try:
  for line in stdin:
      if not line.strip():
          print()
            continue

      n, m = map(int, line.strip().split())
      for _ in range(m):
          try:
              a, b = map(int, scan().strip().split())
          except MemoryError:
                a, b = 0, 0 # 隨便瞎猜一組數字,亂填

          counter[a] = counter.get(a, False) ^ True
          counter[b] = counter.get(b, False) ^ True

      odd_cnt = sum(counter.values())
      print("YES" if odd_cnt in (0, 2) else "NO")
      counter.clear()
except MemoryError:
  print("YES") # 對,我就猜 YES

 

我用紅色標註起來的部分是我額外添加的,其他都與第一版相同

看上去穩了對吧? 絕對不會再吃 MLE 了,再吃我就倒立吃 ㄕ.....

實際上也確實如此

送出後得到的成績是 NA (score: 94%)

 

#6 那個在第一行就爆記憶體的測資檔 AC ,看來是我猜中答案了,#6 答案就是 "YES"

而 #4 和 #5 則吃了 ValueError,他們回傳的錯誤訊息都一樣

Traceback (most recent call last):
  File "/16380650_b924/code_16380650.py", line 16, in 
    a, b = map(int, scan().strip().split())
ValueError: too many values to unpack (expected 2)

 

對 python 夠熟悉的人應該皺眉了

 

a, b = map(int, scan().strip().split())

這一行程式碼在做的事情是這樣的

1. 讀取一行資料

2. 去掉頭尾多於空白字元

3. 根據空白字元的位置把字串轉換成 list

4. 把透過 map 把 list 的所有元素都轉換成 int,並返回一個迭代器

5. 解包,把裡面的元素依序賦值到 a 和 b 兩個變數

 

這是預設每一行字串只有 2 個數字才能用的寫法

如果 split() 後會超過 3 個或以上的元素,就會因為第三個以後的元素都沒有變數可以賦值,發生 ValueError: too many values to unpack

這段英文的意思用白話文描述,就是「太多元素需要解包了,只有 2 個不夠放」

 

啊題目不是在說明中保證每一行就兩個數字嗎,怎麼到這裡變一大堆都塞同一行?

這邊回傳的訊息足夠證明題目的測資格式是有問題的,不符合題意

 

c++ 能 AC 純粹是因為 scanf 不在乎空白字元是空格還是換行符 \n,反正就是一率跳過,所以就算測資檔格式不正確依然能作答

但 python 沒有相同功能的函數,就只能一次讀一整行然後爆記憶體

 

至此已經確認測資檔就是有問題的,可以不用再試了,但我想再猜一把,娛樂一下

於是新的版本出現了

from sys import stdin

scan = stdin.readline

elements = set()
try:
  for line in stdin:
      if not line.strip():
          print()
            continue

      n, m = map(int, line.strip().split())
      for _ in range(m):
          try:
              a, b = map(int, scan().strip().split())
          except (MemoryError, ValueError):
                continue

          if a in elements:
              elements.remove(a)
          else:
                elements.add(a)

          if b in elements:
              elements.remove(b)
          else:
                elements.add(b)

      print("YES" if len(elements) in (0, 2) else "NO")
        elements.clear()

except MemoryError:
  print("YES")

 

我不再使用 bool 紀錄一個數字的總數是否為奇數,而是改用 set 紀錄總數為奇數的元素數量

如果讀到的數字不在 set 內,就加進去,反之則移除,這樣就可以保證 set 內一定只有總數奇數的元素

這樣改的目的是希望盡可能壓縮記憶體占用的空間,確保我只有保存奇數

 

try 的部分,除了 MemoryError 外,我多捕捉了一個 ValueError 應對有超過 3 個以上的數字放在同一行的問題

如果遇到就直接 continue 跳過

 

預期這段程式碼應該會吃 EOFError ,因為如果有幾行漏掉換行符,那 for 循環 m 次就會讀過頭。

但實際結果是在 #4 得到 TLE,然後就沒有然後了。

 

看來是 10^7 這麼多行還是太多了,沒辦法在時間內處理完。

這也是我開頭說 python 正常來說應該沒辦法 AC 這題的原因

因為忽視了 MLE,後面還有 TLE 在等著......

 

除非......你像我一樣,瞎猜答案,猜中了就有機會 AC......