NOIP 2018 普及组 解题报告
比完小結
今年的題目出的有點詭異,難度跨越有點大
入門 to 普及- to(注意:前方東非大裂谷,請小心慢行) 提高+/省選- to 提高+/省選-
不過實際上沒有這么難
T3、T4 一個DP 一個暴力(雖然不是正解) 也就可以過了
扯入正題
T1 標題統計
這道題十分的水,沒有什么技術含量,隨便怎么搞都可以過。
下面是我直接放代碼了。。。
#include<bits/stdc++.h> using namespace std;char t; int ans(0);int main(){freopen( "title.in", "r", stdin );freopen( "title.out", "w", stdout );while( ( t = getchar() ) != EOF )if ( t != ' ' && t != '\n' && t != '\r' ) ans++;printf( "%d", ans ); return 0; }T2 龍虎斗
這道題沒話說,只是題目長了點,好好理解一下也是不難的。
我們可以預處理出兩邊陣營的氣勢和(別忘了加上“某一刻天降神兵”)然后枚舉每個兵營,把你的兵加進去,算出之后兩個陣營最終的氣勢,然后選出氣勢之差絕對值最小的哪個陣營就可以了。
C++內置的函數abs由于老是忘掉是什么類型的,所以干脆手打算了。。。
話不多說,直接上代碼(普及- 及以下難度的不用具體講吧?)。還有注意要開 long long。(死了也別忘記)據說沒開long long只能得70左右。
T3 擺渡車
看這道題的時候,我(相信大家也是這樣)最先想到的是貪心,但是從數據范圍可以看出,如果是貪心題,數據范圍不會那么小(相信NOIP不會和Luogu月賽一樣,2018 11月月賽 搞個幾百大小數據騙我們用DP,結果是貪心)。有些人會想(including me),是不是在有人到達時才能發車呢???沒想清楚就下手的話,就會浪費好多時間。仔細想想,很容易發現不一定要有人到達時發車,比如有時候,bus一回來,有個人等了2分鐘,后面那個人還有INF(hh) min 才會來,如果有人到達時才能發車,那么bus將在INF min后才等到一個人,原來等了2分鐘的那個人與司機等得花都謝了,所以這時候肯定是一回來就發車,雖然沒有人剛好到達。
當然,我們先排序。(DP,從排序做起)。
我們用f[i]表示i min 時發一輛車,ps[i]表示1 ~ i 的人數,ts[i]表示1 ~ i 所有人開始等的時間之和。
設上一次發車是jmin時,那么j min及以前的人都已經滾粗了,我們要求j + 1 ~ i所有人等待時間之和。
等待時間之和為Σ(i - t[k]) 可以簡化為 i * 人數 - Σ(t[k]), 人數、Σ(t[k])可以用前綴和來維護(即前面提到的ps、ts數組)。
然后就可以得到轉移方程——
藍鵝,這樣的復雜度達到了O(MAXT ^ 2)!!!這是遠遠不行的。所以我們要進行優化~
優化I
對于兩個人a、b( ta < tb,b = a + 1) 如果 tb - ta >= 2 * m 可以從中間斷開
如果用work( l, r )表示對l、r區間范圍內進行一次DP work( ls, ta ), ls = tb;
很容易解釋,因為如果兩個人之間時間間隔不小于2m的話,他們是完全可以分兩趟車走的。因為a最遲走的時間為ta + m - 1,車回來的時間為ta + 2m - 1,如果tb >= ta + 2m - 1,剛好可以直接粗發QAQ。(或者理解為b可以作為起點)
優化II
i - 2* m + 1 <= j <= i - m
不難理解,每兩趟車之間間隔不會超過或等于2m(否則中間為什么不再來一趟呢???)
實際上,這已經滿足題目的時間復雜度要求,但是還有一個亂搞優化。(在考場上想到的)
優化III
if ps[i] == ps[i - 1]
? j = i - m
解釋這個優化要從貪心的角度考慮。
回到原來那個問題:是不是在有人到達時才能發車呢???
前面已經解釋了答案是否定的。這從一個側面告訴我們,如果不是在有人到達時才發車,肯定是由于車來不及回來。
所以,在最優方案中,那趟車一定會在剛好回來時發車。也就是說,i min時車剛好回來,上一次發車是在 i - m min時。
真是玄之又玄。
最終的程序不是在考場中寫的,因為考場中 優化I 采用了路徑壓縮的方法,但是出現一些問題,被卡掉20分。
算法的復雜度也是在O(nm)級別,但常數要比Sooke大佬的代碼大一些。
然后上代碼!(雖然不是最好的解,但87ms也湊合吧。)
#include<bits/stdc++.h> using namespace std; #define MAXN 505 #define MAXM 205int n, m, mm, ans; int t[MAXN], ps[MAXM * MAXN], ts[MAXM * MAXN]; int f[MAXM * MAXN];void work( int l, int r ){memset( ps, 0, sizeof ps ), memset( ts, 0, sizeof ts ), memset( f, 0, sizeof f );for ( int i = l + 1; i <= r; ++i ){t[i] -= t[l]; ps[t[i]]++; ts[t[i]] += t[i];}t[l] = 0; ps[0]++;for ( int i = 1; i < t[r] + m; ++i ) ps[i] += ps[i - 1], ts[i] += ts[i - 1];for ( int i = 1; i < t[r] + m; ++i ){if ( i < m ){ f[i] = ps[i] * i - ts[i]; continue; }if ( ps[i] == ps[i - 1] ){ f[i] = f[i - m] + ( ps[i] - ps[i - m] ) * i - ( ts[i] - ts[i - m] ); continue; }f[i] = INT_MAX;for ( int j = max( 0, i - mm + 1 ); j <= i - m; ++j ) f[i] = min( f[i], f[j] + ( ps[i] - ps[j] ) * i - ( ts[i] - ts[j] ) );}int cur(INT_MAX);for ( int i = t[r]; i < t[r] + m; ++i ) cur = min( cur, f[i] );ans += cur; }int main(){scanf( "%d%d", &n, &m ); mm = m << 1;for ( int i = 1; i <= n; ++i ) scanf( "%d", &t[i] );sort( t + 1, t + n + 1 );int ls(1);for ( int i = 1; i < n; ++i )if ( t[i + 1] - t[i] >= mm ) work( ls, i ), ls = i + 1;work( ls, n );printf( "%d\n", ans );return 0; }非常抱歉,雖然這個代碼通過了一些民間數據,但CCF的數據實在太強,把我原來的代碼卡到了80分,以上代碼70分,具體錯誤還不清楚,所以就暫時留坑QAQ。
updata 2018/11/30 23:03: 總算填好坑了QAQ
T4 對稱二叉樹
這道題我也不知道正解是什么。我直接暴力+剪枝也跑過了所有測試點(數據太水?)
我們可以考慮當將一棵樹所有節點的左右子樹交換,那么搜索的順序左變右,右變左。
即原來對于節點P,從根節點到P的路徑是左右左左右,那么反轉后根節點到P'的位置的路徑就是右左右右左。
我們直接枚舉每個子樹的根節點,把原來的點、翻轉后的點一一對應就可以了。
實現起來也不難。注意加點剪枝(不解釋剪枝原理了)。
#include<bits/stdc++.h> using namespace std;const int MAXN = 1000000 + 0xac;//AC萬歲!!!int n, v[MAXN], d[MAXN], s[MAXN]; unsigned long long M1[MAXN], M2[MAXN], M3[MAXN]; int L[MAXN], R[MAXN];void DFS( int x, int dep ){s[x] = 1; M1[x] = v[x]; M2[x] = v[x]; M3[x] = dep * v[x];if ( L[x] != -1 ) DFS( L[x], dep + 1 ), s[x] += s[L[x]], M1[x] *= M1[L[x]], M2[x] += M2[L[x]], M3[x] += M3[L[x]];d[x] = dep;if ( R[x] != -1 ) DFS( R[x], dep + 1 ), s[x] += s[R[x]], M1[x] *= M1[R[x]], M2[x] += M2[R[x]], M3[x] += M3[R[x]]; }bool check( int x, int y ){if ( v[x] != v[y] || s[x] != s[y] || M1[x] != M1[y] || M2[x] != M2[y] || M3[x] != M3[y] ) return 0;if ( L[x] > 0 || R[y] > 0 ){if ( L[x] < 0 || R[y] < 0 ) return 0;if ( !check( L[x], R[y] ) ) return 0;}if ( R[x] > 0 || L[y] > 0 ){if ( R[x] < 0 || L[y] < 0 ) return 0;if ( !check( R[x], L[y] ) ) return 0;}return 1; }bool cmp( int x, int y ){return s[x] > s[y]; }int main(){freopen( "tree.in", "r", stdin );freopen( "tree.out", "w", stdout );scanf( "%d", &n );for ( int i = 1; i <= n; ++i ) scanf( "%d", &v[i] );for ( int i = 1; i <= n; ++i ) scanf( "%d%d", &L[i], &R[i] );DFS( 1, 1 );int ans(1);for ( int i = 1; i <= n; ++i )if ( s[i] > ans && check( i, i ) ) ans = max( ans, s[i] );printf( "%d\n", ans );return 0; }鳴謝
感謝葉康杰童鞋的審核。
感謝CCF提供的數據(尤其是把我T3卡掉了的那些QAQ)。
轉載于:https://www.cnblogs.com/louhancheng/p/10023188.html
總結
以上是生活随笔為你收集整理的NOIP 2018 普及组 解题报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS底层面试题--RunLoop
- 下一篇: 在Object-C中学习数据结构与算法之