1. 程式人生 > >NOIP2018普及組解題報告

NOIP2018普及組解題報告

解題報告

涼涼記

洛谷 5015 標題統計

程式碼(題目過水)

#include <cstdio>
#include <cctype>
using namespace std; int ans; char c; int main(){ while ((c=getchar())!=EOF) ans+=isalnum(c)>0; return !printf("%d",ans); }

洛谷 5016 龍虎鬥

題目連結

分析

有許多蒟蒻都漏掉了m號的情況,結果可能就與一等獎失之交臂,思路就是對於每一個節點判斷最小值。(我朋友竟然打了二分,與一等獎失之交臂,當然,這是後話)


程式碼

#include <cstdio>
#include <cctype>
#define rr register using namespace std; typedef long long bnt; const int N=100010; int n,m,choi,a[N+1],ans; bnt ans1,ans2,sum; inline int iut(){ rr int ans=0; rr char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar(); return
ans; } int main(){ n=iut(); for (rr int i=1;i<=n;++i) a[i]=iut(); m=iut(); rr int x=iut(); a[x]+=iut(); choi=iut(); for (rr bnt i=1;i<m;++i) ans1+=(m-i)*a[i]; for (rr bnt i=m+1;i<=n;++i) ans2+=(i-m)*a[i]; ans=m; sum=ans1<ans2?ans2-ans1:ans1-ans2;//m號初始化 for (rr bnt i=1;i<m;++i){//軒軒 ans1+=(m-i)*choi; rr bnt t=ans1<ans2?ans2-ans1:ans1-ans2; if (t<sum) sum=t,ans=i; ans1-=(m-i)*choi; } for (rr bnt i=m+1;i<=n;++i){//凱凱 ans2+=(i-m)*choi; rr bnt t=ans1<ans2?ans2-ans1:ans1-ans2; if (t<sum) sum=t,ans=i; ans2-=(i-m)*choi; } printf("%d",ans); return 0; }

洛谷 5017 擺渡車

題目連結

分析(線性dp)

作為這套題最難的一道題,我差點悲劇,不過這道題是一道dp的題目,設 d p [ i ] [ j ] dp[i][j] 表示第 i i 個同學(排序後)等車 j j 分鐘後的最短等待時間,那麼在同一段的時候 d p i , j = d p i 1 , t i t i 1 + j + j dp_{i,j}=dp_{i-1,t_i-t_{i-1}+j}+j
要新開一段的時候 d p i , j = m i n t i 1 + k + m t i + j { d p i 1 , k } + j dp_{i,j}=min_{t_{i-1}+k+m\leq t_i+j}\{dp_{i-1,k}\}+j
直到現在,時間複雜度已經是 O ( n m 2 ) O(nm^2) ,已經比較高效了,但是按照dalao的思路,可以用字首最小值優化 k k 的時間,優化至 O ( n m ) O(nm)


程式碼(線性dp)

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
int dp[2][201],ans=1e8,n,m,a[501];
inline int iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
    return ans;
}
int main(){
    n=iut(); m=iut();
    for (rr int i=1;i<=n;++i) a[i]=iut();
    sort(a+1,a+1+n);
    for (rr int i=1;i<=n;++i){
    	dp[i&1][0]=1e8;
    	for (rr int j=0;j<=min(a[i]-a[i-1]-m,m-1);++j) dp[i&1][0]=min(dp[i&1][0],dp[1-(i&1)][j]);
        for (rr int j=1;j<2*m;++j){
        	dp[i&1][j]=dp[i&1][j-1];//字首最小值
        	if (a[i]-a[i-1]+j>=m&&a[i]-a[i-1]+j<3*m)
            dp[i&1][j]=min(dp[i&1][j],dp[1-(i&1)][a[i]-a[i-1]+j-m]);//新開一段
        }
        for (rr int j=0;j+a[i]-a[i-1]<2*m;++j)
            dp[i&1][j]=min(dp[i&1][j],dp[1-(i&1)][j+a[i]-a[i-1]]);//同一段
        for (rr int j=0;j<2*m;++j) dp[i&1][j]+=j;//無論開不開都要加等待時間
    }
    for (rr int i=0;i<m;++i) ans=min(ans,dp[n&1][i]);//取最小值
    return !printf("%d",ans);
}

分析(斜率優化)

斜率優化與上面的線性dp迥然不同,而且對於 O ( n m ) O(nm) 這麼小的情況下, O ( m a x { t i } ) O(max\{t_i\}) 太大了,不過為了培養更多的思維,在此講一下斜率優化的方法(肯定非高效正解)
d p [ i ] dp[i] 表示到第 i i 時刻的總等待時間, c n t [ i ] cnt[i] 表示到第 i i 時刻的同學個數, s u m [ i ] sum[i] 表示在第 i i 時刻時發車的總等待時間
那麼 d p [ i ] = m i n { d p [ j ] + ( c n t [ i ] c n t [ j ] ) i ( s u m [ i ] s u m [ j ] ) } dp[i]=min\{dp[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j])\}
然而這樣肯定會超時,若使 k ( j &lt; k ) k(j&lt;k) 更優秀,那麼
d p [ k ] + ( c n t [ i ] c n t [ k ] ) i ( s u m [ i ] s u m [ k ] ) &lt; d p [ j ] + ( c