因為前幾天作死立了一個flag說要把NOIP近十年的題目做一做,並寫一個題目歸類+題解摘要出來,所以這幾天就好好的(然而還是頹廢了好久)寫了一些這些往年的NOIP題目。
這篇博客有什么:
近十年NOIP題目歸類+簡要題解+AC程序+相似題目(雙倍經驗之類的)
備注:
但是因為本蒟蒻實在太蒻了,好多題目只會打暴力拿部分分數,想不到正解,所以在寫題的時候參考了一些博客和題解,所以自然這篇博客也借用了一部分dalao的思路和想法(但是最后還是本蒟蒻獨立完成的就是了),在此對各位提供幫助的dalao表示深深的感謝。
博客里面按照分類加入了NOIP題目鏈接以及憑借本蒟蒻做題看到的一些相似題目鏈接,題目鏈接來源於洛谷。
應該是更新完了。。。如果有哪個題忘了寫,還請dalao們在評論區指出。
搜索,模擬:
1、2008T2 火柴棒等式(打表)(搜索)
就是簡單的搜索(貌似連剪枝都不需要),但是也可以打表(打表大法好!!)在此就不放題解了。
2、2009T1 潛伏者(字符串)(模擬)
字符串模擬水題,不放題解了。
3、2009T4 靶形數獨(搜索)
題解什么的已經以專題的形式寫過了,在這里放鏈接好了: 數獨問題
4、2010T1 機器翻譯(搜索)(隊列)
還是水題,就不放題解了。
5、2010T4 引水入城(搜索)(動態規划)
這道題有兩問,求沒有被覆蓋到的沙漠地帶可以用搜索來看下面有沒有被完全覆蓋到,直接dfs+判斷即可。但是求需要的蓄水站需要我們求出來不可能被覆蓋的區域個數。
本來想的是用貪心求解,但是因為覆蓋區間有重復,所以有點麻煩。
因為只有最后一行需要水源覆蓋,所以我們需要記錄每個點能夠到達最后一行的區間。這個操作可以用dfs來實現。但是考慮到dfs可能時間復雜度有點高,所以我們采取記憶化搜索(開數組記錄能夠到達最左邊和最右邊的位置)。
為了解答第二問,我們需要做區間覆蓋,核心代碼如下:
while (left<=m){
int maxr=0;
for (int i=1;i<=m;i++)
if (l[1][i]<=left)
maxr=max(maxr,r[1][i]);
//尋找左端點在當前點之前並且右端點最靠右的區間
cnt++;
//找到一個區間(一個蓄水站可以覆蓋的區間)答案加一
left=maxr+1;
//更新當前點
}
5、2011Day1T1 鋪地毯(模擬)(離散化)
這是水題,就是倒着枚舉。但是和它思想類似的還有HAOI2014貼海報
6、2011Day1T3 Mayan的游戲(搜索)(模擬)
這個題目可真惡心,怎么這么考驗代碼能力啊
寫這個題一定要明白將函數分開寫的好處!要不然寫一會兒就雲里霧里,不知道自己在干什么了。
介紹函數:
cut_off:
搜索有沒有可以消去的相連方塊,如果有就將可以消去的方塊消去並且返回true,沒有就返回false。
這里需要注意的是因為存在諸如圖5情況,所以遍歷到有連着三個或者以上的方塊一定不要立刻消去,而應該采取打標記的方法,之后再消去。

這里我們采取從選定格子向四周拓展的方法,暴力地遍歷判斷是否有格子需要消去,並將其打上標記。(暴力代碼寫的真長,我太蒻了)
fall:
這個是消去格子之后使得懸空的格子降落的函數
clear_up:
這個是判斷是否符合游戲結束標准的函數(也就是遍歷一遍,看看是不是都空了)
move:
執行移動的函數。(在這里需要注意的是有可能移動之后會造成再次消去的情況,所以用while()來確保進行一系列操作后所以可以消去的情況都已經被執行過了)
search:
這個是核心啊。(最惡心的搜索)
題目要求的是按照字典序輸出,所以我們從最左邊開始搜索,先往右邊走,在往左邊走。
有一個小小的剪枝,就是顯然如果兩個格子顏色一樣,就不需要交換了。
但是這個貌似會T,看了題解之后才知道如果左邊有格子就不需要再往左邊搜索了,因為我們是從左邊搜索過來的,那個時候就已經搜索和右邊格子交換的情況了。
當然我們需要一個輔助數組來記錄每一步的狀態ans,在回溯的時候使用。(這里使用\(ans[i][j]\)m,其中i表示步數,j(j\(\in{}\)[1,3])分別表示需要輸出的x,y,移動方向)
標程
7、2012Day1T1 Vigenère 密碼(模擬)(字符串)(map)
開map是水題,就是寫起來比較麻煩(比如蒟蒻我,一個一個插入的,累死了)
8、2013Day2T1 積木大賽(模擬)(貪心)
水題。就是從左到右掃一遍,不到高度的就補,到了高度就continue就可以了。
(值得吐槽的是這個是我當初高一加入我們學校機器人社團編程組的面試第三題,我當時高高興興地以為在社團編程也是競賽之類的東西.......然后發現完全不一樣好不好!所以說雖然很簡單,但是為什么機器人編程組要考一道NOIP競賽貪心題啊qwq(偷偷吐槽primy他應該不會知道吧))
9、2013Day2T3 華容道(搜索)(圖論)
關鍵是記錄狀態的轉移和建圖操作;
題解:https://www.cnblogs.com/fengxunling/p/9773648.html
10、2014Day1T1 生活大爆炸版石頭剪子布(模擬)(字符串)
水題。
11、2014Day2T1 無線網絡發射器選址(模擬)
水題。
12、2015Day1T1 神奇的幻方(模擬)
水題。
13、2015Day1T3 斗地主(搜索)(模擬)(貪心)
其實很水,就是細節有點多。。。注意不是純搜索+剪枝,還要有貪心的。。。。
題解:https://www.cnblogs.com/fengxunling/p/9862768.html
14、2016Day1T1 玩具謎題(模擬)
一個稍微麻煩一點的模擬。
15、2016Day2T3 憤怒的小鳥(搜索)(狀壓DP)
雖然說有狀壓DP的做法。。。但是蒟蒻不會啊!要不然回來再補狀壓DP的做法好了qwq(咕咕咕)
這里介紹的是一種搜索的方法——
就是最簡單的搜索啦~~(fake),爆搜肯定過不去啊,所以我們要考慮很多魔性剪枝。
我們構造一個搜索,用dfs(c,u,v)表示當前搜到第c只豬,已構造拋物線的數量為u,單獨的豬的數量為v時的情況。
這道題做的時候參考了 我在學習 dalao的題解,他的題解超詳細的說,如果不懂的話完全可以去參考戳我
注意我們計算有二次函數計算,所以涉及到了浮點小數,所以注意判斷相等的操作的時候不要直接“==”!!
16、2017Day1T2 時間復雜度(模擬)(棧)
就是一個大模擬,但是由於循環可以嵌套,所以我們考慮棧的數據結構。。。。為了方便,我們可以開兩個棧,一個來存名字,一個來存時間復雜度。
然后我們可以開兩個函數,一個判斷是否有名字重復,一個判斷時間復雜度是否符合條件。
然后就是各種特判啦考試的時候這種模擬題一定要多造幾組數據爭取hack掉自己的程序,要不然極其容易WA掉啊!(比如說我第一次交才36分吶)
注意輸入沒有輸入完的話,一定不要輸出答案然后continue或者break掉,這樣會RE的!!
下面是我的代碼(帶了超詳細的輸出調試信息),自認為還是比較好理解+簡潔的(fake)
標程
數學:
1、2008T1 笨小猴(素數判斷)(排序)
素數判斷+快速排序,就是很簡單數學題,素數判斷寫最簡單的那種就行。
2、2009T2 Hankson的趣味題(gcd,lcm)
這個題如果想跑暴力分數貌似還是蠻高的,但是正解需要一個結論:
對於兩個正整數a,b,如果\(gcd(a,b)=k\),那么\(gcd(a/k,b/k)=1\)。
知道了這個結論之后,我們要嘗試應用:
\(lcm(x,b0)=b1\)
\(gcd(x,b0)=\frac{x*b0}{lcm(x,b0)}=\frac{x*b0}{b1}\)
\(gcd(\frac{x*b1}{x*b0},\frac{b0*b1}{x*b0})=1\)
\(gcd(\frac{b1}{b0},\frac{b1}{x})=1\)
同理:
\(gcd(\frac{x}{a1},\frac{a0}{a1})=1\)
綜合以上兩個式子得到以下結論:
x是a1的整數倍而且也是b1的因子。
所以我們可以枚舉b1的因子(而且只需要枚舉到\(\sqrt{b1}\)就可以了,因為它是因子,所以后面的我們可以通過b1處以它來獲得),然后判斷是不是a1的整數倍就可以了。
然后注意一下如果不是\(\sqrt{b1}\)的話ans+=2,如果正好是的話只能增加一個答案。
3、2011Day2T1 計算系數(楊輝三角)
就是很水的數論題,用到了數學必修三的知識楊輝三角。
那么就不放題解了qwq。
4、2012Day1T2 國王游戲(數學)(高精)(排序)
通過數學推論我們可以知道要把左右手乘積按照從大到小排序才是最優解。
然后要高精度乘法,高精度除以低精度。
這里的高精度可以壓位。
5、2012Day2T1 同余方程(拓展歐幾里得)
ecgcd模板題。
6、2013Day1T2 火柴排隊(排序不等式)(逆序對)(離散化)
做這個題需要知道排序不等式相關知識:正序和>=亂序和>=逆序和,
除此之外還要知道:如果將原本亂的數組按升序排序,並且每次只能交換一對數字的話,需要的次數是原序列逆序對的數量。
所以我們可以采用歸並排序或者樹狀數組求逆序對(反正我是不會樹狀數組求逆序對)
不會求逆序對的來打模板---->模板
但是需要注意的是,這個題需要離散化!!
由於在離散化這里跪了,所以蒟蒻我只好取翻題解(哦,對了,順便感謝BLUESKY007大佬的解釋十分優秀地打好了輔助),題解里 ZJYelizaveta大佬是這么說的:
“這道題目的精華在於對於新建序列!
假設我們現在有離散化后的序列$ a={4,3,1,2}\(,\)b={1,3,2,4}$。
我們令 q[a[i]]=b[i],相當於以 a[i]為關鍵字對序列 b[i] 排序。
若序列 aaa 與序列 bbb 相等,那么此時 q[a[i]]應該等於 a[i] 的,也就是 q[i]=iq[i] = iq[i]=i。
那么也就是說如果我們想讓序列 a 與序列 b 相等,那么我們需要讓 q升序排列。
問題就變為,將原本亂的 q 序列升序排列的最少交換次數。”
2014Day2T3 解方程(秦九韶算法)(讀入優化大數取模)
qwq其實也沒什么,就不放代碼了qwq
2016Day2T1 組合數問題(楊輝三角)
其實只要看出來是楊輝三角,預處理出來一個楊輝三角的表,就是水題啦~~
分治
1、聰明的質檢員(二分)(前綴和)(遞推)
這個題目的題我真的是沒有看懂(圖片是什么意思qwq),在這里貼上別人的解釋吧:
s是區間中w[i]>=W的個數,v是所有滿足w[i]>=W的i的v[i]的和,那么這個貢獻就是s*v。
那么就沒有什么了,我們使用前綴和算出價值 sum[i]=sum[i-1]+v[i],數量 ge[i]=ge[i-1]+1(注意如果重量沒有達到,不添加此次的價值和個數)
最后就是一個二分答案,查找和最佳答案。
標程
2、2012Day2T2 借教室(二分)(前綴和)
3、2013Day1T1 轉圈游戲(快速冪)(數學)
快速冪水題
4、2015Day2T1 跳石頭(二分)
水題,不說了。
順便一提,雙倍經驗丟瓶蓋
倍增
1、2012Day1T3 開車旅行(倍增)
題解:https://www.cnblogs.com/fengxunling/p/9755195.html
2、2013Day1T3 貨車運輸(LCA)(最大生成樹)
在下面圖論類別里有詳解。
3、2012Day2T3 疫情控制(倍增)(樹)(二分)
比較綜合+碼量很大的題qwq
題解:https://www.cnblogs.com/fengxunling/p/9759052.html
動態規划,遞推:
imone dalao曾說,如何判斷一道題是不是動態規划呢......可以參考數據范圍,如果在三位數四位數左右就可以想動態規划了,而且狀態設計同樣則可以結合數據范圍和變量,先想出開幾維的數組,然后結合做題經驗進行狀態設計。(當然有時候可以運用滾動數組將一個維度滾動掉)
據本蒟蒻的觀點來講,DP最重要的是狀態設計和初始化......然后DP有很多種類啊,蒟蒻我題目見的也不多,好多題目也不會做。(對啦,在這里先orz機房DP大佬soul_M一波)
1、2008T3 傳紙條(高維DP)
這是一個高維DP的題目,可以將將尋找矩形的一對對角頂點出發走到另一個對角頂點的不重復路線(兩條)轉換為從同一個頂點走到它的對角頂點的兩條最短嚴格不相交路線。
注意由於終點的值是0,所以目標狀態就是\(f[n][m-1][n-1][m]\)。(因為終點是邊角啊,所以只有從終點上邊或者左邊過來qwq)
有一個叫做方格取數的題目和它很相似,可以水一波雙倍經驗-------->**方格取數 **
(聽說還可以用費用流做?反正我不會)
2、2010T2 烏龜棋(高維DP)
因為只有四種前進方式,而且數據范圍也不大,所以我們考慮開四位數組,每一維分別表示對應卡片運用了多少次。
注意初始化和起始位置為1。
這個代碼加了讀入優化(看起來有點長的樣子)
標程
3、2011Day2T3 觀光公交(遞推)(貪心)
這個題目一看就是貪心,但是當年NOIP卻有錯誤的貪心策略AC本題的情況(出題人估計是沒有想周全),但是這並沒有關系,因為落咕添加了hack數據!(毒瘤)
標程//標程里面有簡要注釋
4、2013Day2T2 花匠(動態規划)(貪心)
其實我是用貪心A掉的,所以說我也不會動態規划的做法。之所以放到這里是因為好多人都說它是動態規划,而且我也沒有開貪心的標簽qwq。
因為用貪心的方法做感覺這題比較水,就不放題解了qwq。
5、2014Day1T3 飛揚的小鳥(背包)(動態規划)
這個題目就是上升的話因為可以無限次點擊,所以是完全背包;下降的話只有一次,所以是01背包。
注意問題模型的抽取與簡化,注意處理飛到天花板(或者更高)的情況處理即可。
貌似有人說搜索也可以?反正我是不會啦
6、2015Day2T2 子串(動態規划)(枚舉)
先來設計狀態:
設s[ i ][ j ][ k ]為A用到了 i ,B用到了 j ,已經用了 k 個子串, 並且一定用了當前字符(A[i])時的方案數。
設f[ i ][ j ][ k ]為A用到了 i ,B用到了 j ,已經用了 k 個子串, 無論用不用當前字符(A[i])時的方案數總和。
注意直接開數組是開不下的,所以要用滾動數組將其第一維滾動掉。
7、2016Day1T3 換教室(期望DP)
我們定義dp[i][j][0/1]來表示當前為第i個階段,連同這一次已經用了j次換教室的機會,當前這次換(1)不換(0)的最小期望路程總和。
預先用floyd處理出教室之間的距離,然后然后狀態轉移的時候注意概率的問題(使用乘法分配律)
轉換方式是這樣的:
for(int i=2;i<=n;i++){
double add1=f[c[i-1]][c[i]];
for(int j=0;j<=min(m,i);j++)
{
dp[i][j][0]=min(dp[i-1][j][0]+add1,dp[i-1][j][1]+f[d[i-1]][c[i]]*p[i-1]+f[c[i-1]][c[i]]*(1-p[i-1]));
if(j!=0)
dp[i][j][1]=min(dp[i-1][j-1][0]+f[c[i-1]][d[i]]*p[i]+f[c[i-1]][c[i]]*(1-p[i]),dp[i-1][j-1][1]+f[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+f[c[i-1]][d[i]]*(1-p[i-1])*p[i]+f[d[i-1]][c[i]]*(1-p[i])*p[i-1]+f[d[i-1]][d[i]]*p[i-1]*p[i]);
}
}
8、2017Day1T3 逛公園(圖論)(DP)(記憶化搜索)
題解:https://www.cnblogs.com/fengxunling/p/9860984.html
圖論:
1、2008T4 雙棧排序(二分圖染色)
這道題說實話我是不會做的.......只能看題解啊qwq
(以下有很多話都是摘自題解的,侵刪,侵刪!)
(看了題解之后發現竟然是個圖論題!二分圖染色+模擬。)
如果a[i]和a[j]不能在一個棧內,即連接一條i與j之間的無向邊,接下來我們只需要判斷這個圖是否為二分圖
由於題目中說編號的字典序要盡可能的小,那么就把編號小的盡可能放到stack1
判斷二分圖的方法可以采用黑白染色的方式,先從編號小的開始染,第一個頂點染成黑色,相鄰的頂點染成不同的顏色,如果發現黑白沖突,那么說明這個圖不是一個二分圖,是不合法的,輸出0.
染色后所有黑色的點進stack1,所有白色的點進stack2,最后模擬輸出過程就可以了.
(而如何判斷能不能在一個棧內:a[i]和a[j] 不能壓入同一個棧\(⇔\)存在一個k,使得
$ i<j<k$ 且 $a[k]<a[i]<a[j] $,考慮進一步優化
狀態:\(f[i]=min(a[i],a[i+1], ... ,a[n])\)
邊界條件:\(f[n+1]=INF;\)
狀態轉移方程:\(f[i]=min(f[i+1],a[i]);\)
於是上述判斷就轉化為了\(f[j+1]<a[i]\) &&$ a[i]<a[j]$
2、2009T3 最優貿易(分層圖)
這是一個分層圖的問題,需要構建分層圖+dfs(或者bfs)+spfa最短路。
(話說還有個分層圖的經典題目------> 飛行路線
(飛行路線雙倍經驗)改造路
因為我們要執行買入和賣出兩個操作,買入之后就不會再買,所以說買入的單次操作之間是獨立的,同理賣出的單詞操作之間也是獨立的。所以我們可以考慮建立分層圖。
建立了分層圖之后可以保證買入和賣出的操作只進行一次而不是無限多次,(因為買入之后會進入下一層圖,賣出之后也會進入下一層圖)
我們設第一層圖(1~n)是什么都不操作的圖,第二層(n+1~2n)是買入的圖,第三層(2n+1~3n)是賣出的圖。在同一層圖進行移動的時候不花費金錢(因為沒有進行任何操作),而從第一層進入第二層需要花費金錢(所以我們將第一層和第二層相對應的點(比如說i和i+n)連接需要花費金錢的負邊權,同理我們將第二層和第三層相對應的點連接賣出金錢的正邊權,之后跑spfa就可以將所有情況覆蓋了)
不過也需要注意一點就是買入一定要賣出,但是也可以不買入不賣出(就是不進行任何操作),所以我們要連接一個超級源點3n+1。
這里放上來自fy1234567ok大佬的分層圖圖片:

借助上圖可以更好的理解分層圖的方法。
標程
3、2010T3 關押罪犯(並查集)(排序)
本題開兩個並查集,先按照沖突值從大到小排序,(沖突值高的優先視作敵人)本着敵人的敵人就是我的朋友的原則,記錄每個人的敵人,合並朋友,如果遍歷到一個人是另一個人得敵人的話,就將那個人和對方的敵人合並。
相似思路NOI2001食物鏈 BOI2003團伙
4、2014Day1T2 聯合權值(LCA)(乘法分配律)
這個題就是建圖之后,找到每個點,然后將它的子節點兩兩的乘積加到一起......因為遍歷的話需要\(n^2\)復雜度,我們可以考慮乘法分配律優化,注意記錄最大值和次大值。
核心代碼如下:
for(int i=1;i<=n;i++)
{
if(v[i].size()<=1) continue;
int cur1=-1,cur2=-1;
for(int j=0;j<v[i].size();j++)
{
ans[i]=(ans[i]+sum[i]*w[v[i][j]])%MOD;
// 乘法分配律優化
sum[i]=(sum[i]+w[v[i][j]])%MOD;
if(w[v[i][j]]>cur1)
cur2=cur1,cur1=w[v[i][j]];
else if(w[v[i][j]]>cur2)
cur2=w[v[i][j]];
}
p+=ans[i]*2;
maxx=max(maxx,cur1*cur2);
}
5、2013Day1T3 貨車運輸(最大生成樹)(LCA)
一看到是求兩點之間的路徑,想到圖論。但是用普通的Floyd貌似是時間復雜度會爆炸,所以我們考慮優化。
由於構造樹之后我們可以用LCA倍增求得路徑,所以我們先構造最大生成樹。
構造最大生成樹之前將邊先排序,然后用樹上倍增LCA求出兩點之間的路徑即可。
最后注意不能到達的話輸出-1。
6、2013Day2T3 華容道(搜索)(圖論)
關鍵是建圖的操作不好想。
具體見題解:https://www.cnblogs.com/fengxunling/p/9773648.html
7、2014Day2T2 尋找道路(圖論)(搜索)
雖然說是圖論,但是實際上就是兩遍dfs,第一遍排除不符合性質的點,第二遍求的答案。
8、2014Day1T2 信息傳遞(圖論)(並查集)
這個題當然可以用並查集模擬暴力水80分啦~,但是想要AC的話還是要一定的剪枝!因為我們注意到每一個節點都只有一條出邊,所以所有的節點在一起構成了一個基環外向森林,這個東西就是從一個節點出發,最終必定會進入到一個環中。qwq所以我們可以記錄環,然后遇到相同的就跳過,這樣就大大的降低了復雜度了.
核心代碼:
void search(int now,int bushu)
{
if(pre_v[now]==1) return;
if(v[now]==1)
{
ans=min(ans,bushu-sum[now]);
return;
}
if(!v[now])
v[now]=1;
sum[now]=bushu;
search(fa[now],bushu+1);
pre_v[now]=1;
}
9、2017Day1T3 逛公園(圖論)(DP)(記憶化搜索)
題解:https://www.cnblogs.com/fengxunling/p/9860984.html
數據結構
1、2010Day1T2 選擇客棧(棧)(遞推)
不是一個很難的題目,就不放題解了。
2、2012Day2T3 疫情控制(樹)(倍增)(二分)(貪心)(排序)
題解:https://www.cnblogs.com/fengxunling/p/9759052.html
3、2016Day2T2 天天愛跑步(LCT)(樹剖)(線段樹)
打暴力其實有80分。。如果是在NOIP考場上,80分性價比已經很高了。
推薦去看一下這位大佬的博客:https://www.cnblogs.com/ljh2000-jump/p/6189053.html
寫的真的很好qwq
4、2016Day2T2 蚯蚓(二叉堆)
其實這個題可以用STL水到80分,但是STL慢啊!所以我們可以考慮手寫堆.......加上優化。
但是如果你想抱緊STL的話,其實我們還可以進行優化來達到減時間的效果。
你可以發現,因為每次蚯蚓的長度都會增加,所以說前面切肯定比后面切最后蚯蚓長度長,所以說本來就存在單調性。
我們假設蚯蚓a1,a2,···,我們排序之后滿足a1>a2>····,那么以此分成兩只a11,a12,a21,a22,····那么:a12>a22>···,a11>a21>···;
我們可以將沒有切的蚯蚓,切的蚯蚓長的那一段,短的那一段分別放在三個優先隊列(你想手寫堆我也不反對)里面,然后每一次取出三個里面最短的那個,然后切開之后放在對應的隊列里面。重復操作,直到到時間為止。
5、2017Day2T3 列隊(平衡樹)(樹狀數組)
不好意思,目前還不會寫。回來補。
