1. 程式人生 > >Making the Grade (bzoj1592)題解

Making the Grade (bzoj1592)題解

pen 最優 數據 -1 n) queue 又一 線段樹 我們

  又一次爆零,好欣慰。

  這次這道題講真簡單,然而freopen又一次打錯了,又一次……

  由於這幾天一直在搞網絡流,上來就按照網絡流的思路搞,顯然,搞不出來,於是由於“最小”這個敏感詞語,想到了DP。開搞。

  首先是一個證明,修改完後每條道路的高度一定是原來就有的數據,至少這是最優解之一,由於這道題並未要求打印方案,因此只要確定這一點這道題就做完25%了。

  那麽我們可以先把問題拆開來看,求不上升和求不下降,不上升可以把整個數列倒過來,因此這道題就是輸出不下降序列的最優解了。假設我們只有兩條道路,他們是下降的,那麽我要改變的就是ΔH,我可以把第一個向下壓也可以把第二個向上提也可以,總之改變的為ΔH,而改變為他們的平均數也是ΔH。

  那麽這道題就方便許多了,一開始H≤10^9但N最大為2000,只要離散一下就可以happy的寫出狀態數組了f[2001][2001],這樣就不必擔心內存炸了。而它的含義也顯然了,f[i][j]就代表到第i個點,i高度改為j的花費,因此轉移方程就出來了f[i][j]=min(f[i-1][1~j])+abs(h[i]-j),雖然不太明白那群大佬為什麽要用線段樹搞,但我默默地用了一個變量儲存之前的最優解就搞出來了……,至於不下降,把循環倒過來就好了。

  

 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4
#include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #include<map> 9 using namespace std; 10 map<int,bool> ma; 11 int n,zz1; 12 int a[2005],b[2005]; 13 int jg[2005]; 14 int f2[2005][2005]; 15 int f1[2005][2005]; 16 int main(){ 17 scanf("%d",&n);
18 for(int i=1;i<=n;i++) 19 { 20 scanf("%d",&a[i]); 21 if(!ma[a[i]]) 22 { 23 zz1++; 24 jg[zz1]=a[i]; 25 ma[a[i]]=1; 26 } 27 } 28 sort(jg+1,jg+zz1+1); 29 for(int i=1;i<=zz1;i++) 30 { 31 f1[1][i]=abs(a[1]-jg[i]); 32 } 33 for(int i=2;i<=n;i++) 34 { 35 int mn=0x7fffffff; 36 for(int j=1;j<=zz1;j++) 37 { 38 mn=min(f1[i-1][j],mn); 39 f1[i][j]=mn+abs(a[i]-jg[j]); 40 } 41 } 42 int ans1=0x7fffffff; 43 for(int i=1;i<=zz1;i++) 44 ans1=min(ans1,f1[n][i]); 45 for(int i=1;i<=zz1;i++) 46 f2[1][i]=abs(a[1]-jg[i]); 47 for(int i=2;i<=n;i++) 48 { 49 int mn=0x7fffffff; 50 for(int j=zz1;j>=1;j--) 51 { 52 mn=min(mn,f2[i-1][j]); 53 f2[i][j]=mn+abs(a[i]-jg[j]); 54 } 55 } 56 int ans2=0x7fffffff; 57 for(int i=1;i<=zz1;i++) 58 ans2=min(ans2,f2[n][i]); 59 printf("%d\n",min(ans1,ans2)); 60 return 0; 61 } 62

  考試剛開始一會就想出正解了,總共花了不到一個小時的時間打完的題被我兩秒鐘不到就毀了,啥時候才能長長記性不長肉啊。

Making the Grade (bzoj1592)題解