1. 程式人生 > >重郵第八屆ACM大賽-預賽題解報告

重郵第八屆ACM大賽-預賽題解報告

A. a x b (Ⅰ)

簽到題

  • 學會如何處理多組資料
  • 注意爆int

code

B. 多關鍵字排序

水題

  • 讀入的時候可以考慮使用
scanf(" (%d,%d)", &x, &y);
  • 排序可以直接使用std::sort,當然也可以自己寫,但氣泡排序等高複雜度演算法無法通過該規模的資料;考慮使用快排、歸併排序等高效演算法
  • 可以自己寫結構體,當然這裡推薦使用std::pair;如果自己寫結構體,需要寫cmp函式或過載<運算子

更詳細的STL用法可以自行搜尋或參考Cplusplus
標程裡使用的是讀入掛…
code

C. 全排列

水題
使用std::next_permutation瞬秒

code
當然也可以自己遞迴生成,這裡提供一個js覺得很勁的寫法

D. 三數和為零

一個可行的做法是:

  • 列舉ai
  • 查詢set內是否有ai
  • 將所有的ai+aj(j<i)插入set

這裡使用std::set會超時,所以我手寫了一個hashset code
當然這題做法還有很多,您們可以自己研究(比如使用two pointers,複雜度可以達到O(n2))這裡貼一個js的code

背一下鍋
這道題的資料水了,放了一些錯誤的做法過。
很多人是列舉兩個數ai+aj,然後二分查詢是否存在aiaj,但事實上題目要求這三個數的下標不同,這樣做無法保證。比如:
對於an=

{1,2,0},答案應該是No,但是很多人的程式會輸出Yes,因為列舉-1+2時,在序列中查詢1-2=-1,是找得到的,但是這個-1取了2次。
賽後資料已經更新了,這樣水過的同學可以嘗試改一下。

E. a x b (Ⅱ)

模擬高精度乘法
當然也可以用java的BigInteger水過
由於C++的高精度本弱寫得太醜,就貼個java吧(逃
code

F. 資料結構

  • 對於操作1、2、3、4,我們可以使用std::deque雙端佇列來維護
  • 對於操作5,我們並不需要真的reverse整個序列,只需要用一個變數標記一下。若有標記,則操作1、2互換,3、4互換,標記與否不影響操作6
  • 對於操作6,用一個std::map維護即可

具體參見:
code

G. 矩陣鏈乘

動態規劃經典例題,可參考算導等資料
f[i][j]表示計算矩陣i到j需要的最少的乘法次數,row[i]表示矩陣i的行數,col[i]表示矩陣i的列數,則狀態轉移方程為:

f[i][j]=minik<j{f[i][k]+f[k+1][j]+row[i]col[k]col[j]}f[i][i]=0
時間複雜度O(n3)
標程使用記憶化搜尋實現
code

H. 滿射函式

該問題的另一種等價敘述為:
n個不同的球放入m個不同的盒子中,要求每個盒子中至少有一個球,問有多少種不同的放法。

這和第二類斯特林數(集合的劃分數)很像,事實上答案就是S(n,m)*m!,因為集合劃分實際上是把m個盒子看成相同的,而m個盒子的全排列就是m!
但是本題n和m較大,如果用遞推法求斯特林數會超時
這裡需要使用容斥原理
f(k)表示n個不同的球放入m個不同的盒子中,保證至少有k個盒子中不放球,的方案數
f(k)=Ckm(mk)n,前者表示m個盒子中選k個出來(不放球),後者表示n個球放入剩下的m-k個盒子
利用容斥原理,可以得到:

answer=k=0m(1)kCkm(mk)n
大組合數的計算方法可以使用預處理階乘,求逆元可以使用擴充套件歐幾里得演算法或者費馬小定理+快速冪演算法
當然這題由於組合數下標固定,上標遞增,也可以不預處理階乘,直接遞推
code

I. a x b (Ⅲ)

FFT模板題…
直接模擬乘法複雜度O(n2),會TLE
乘法運算可以看成卷積和運算,事實上卷積和的一種求法就是不進位乘法
使用fft做快速卷積即可,複雜度O(nlogn)
這裡建議手寫complex,比STL自帶的快不少
code

J. 行列式求值

模擬題
回憶線性代數,求行列式值的常用方法是將行列式化為上三角行列式,則行列式的值就是主對角線元素的乘積。在模p域下,加減乘都很好操作,而因為p是質數,所以模p域內任意非0元都存在乘法逆元,除法也可以轉化為乘它的乘法逆元。
code

K. 炸彈爆破

模型可以轉化為一個有向圖
若炸彈A能引爆炸彈B(B的位置在A的爆炸範圍內),則連一條A->B的有向邊
列舉兩個炸彈的組合,可以在O(n2)的時間內完成建圖
建完圖後,求強連通分量,縮點,找入度為0的點
入度為0則表示該聯通分量不可能由其他聯通分量(炸彈)引爆,只能付出代價手動引爆
而要引爆某個連通分量,顯然引爆其中代價最小的炸彈最為划算
code