【NOIP2012-开车旅行】
這道題:你不僅要學會兩人交換開車,還要做到高效駕駛。
?
·分析:
????? 在撥開花哨題目的迷霧之后,發(fā)現(xiàn)兩個重要突破口:
????? ①從每個點開始,他們的路徑是一定的,不存在決策選取。
????? ②要是n,m沒有那么大的話,就直接預處理每個點對于每個人開車至下一個點的位置和路程(n2),然后兩個問題都可以從起點(第一問就是枚舉起點)開始預處理的數(shù)據(jù)來“輪流開車”。(這一個突破口有點過于頂尖了,因為這是過70%數(shù)據(jù)的題解)
????? 下圖是這種做法的簡圖。
????? 用des[i][0],des[i][1]分別表示在城市i小B開車和小A開車前往的下一個目的地。用Min[i][0],Min[i][1]分別與上面的數(shù)組對應(yīng),表示對應(yīng)路徑的長度(注意是反向枚舉)
?????????
·無論是第一問還是第二問,都可以從起點s開始,通過des,Min數(shù)組來向后開車并記錄兩人各自走的路程。
·很美妙但又很遺憾,僅僅這樣做時間復雜度:(n2+nm)
·這道題真正的考點就出來了:考察我們的優(yōu)化技能。
·很輕易可以發(fā)現(xiàn),這道題的數(shù)據(jù)范圍又給了我們很大的提示:
n<=100000。這啟示我們:nlogn
·余下的事情就是把時間復雜度中的部分n替換成logn,使得時間復雜度保持在:O(mlogn+nlogn)【這有點過于頂尖了】
·在本題中大米餅的對策是:預處理des,Min(這原來是一個n2)時使用STL中的set來維護(降為nlogn),在路徑上使用倍增法,
·最后一個值得注意的一點,倍增的每一段為了方便操作,這里AB各走一次算成一段,不過倍增的距離還是對AB進項單獨維護。整個程序還有許多細節(jié)需要注意。 wow!
?
1 #include<stdio.h> 2 #include<algorithm> 3 #include<set> 4 #define go(i,a,b) for(int i=a;i<=b;i++) 5 #define ro(i,a,b) for(int i=a;i>=b;i--) 6 #define inf 2147483645 7 #define eps 0.000003 8 using namespace std;const int N=100005; 9 int n,m,s,h[N],des[N][2],Min[N][2],To[N][22],dis[N][22][2],tot[2],x; 10 struct info{int h,id;bool operator<(const info a)const{return h<a.h;};}; 11 set<info>box;set<info>::iterator I;int A(int t){return t<0?-t:t;} 12 void consider(int i,info p) 13 { 14 int j=p.id; 15 if((A(h[i]-h[j])<Min[i][0])||(Min[i][0]==A(h[i]-h[j])&&h[j]<h[des[i][0]])) 16 { 17 if((Min[i][0]<Min[i][1])||(Min[i][1]==Min[i][0]&&h[des[i][0]]<h[des[i][1]])) 18 Min[i][1]=Min[i][0],des[i][1]=des[i][0]; 19 Min[i][0]=A(h[i]-h[j]),des[i][0]=j; 20 } 21 else if((A(h[i]-h[j])<Min[i][1])||(Min[i][1]==A(h[i]-h[j])&&h[j]<h[des[i][0]])) 22 Min[i][1]=A(h[i]-h[j]),des[i][1]=j; 23 } 24 void doubling(int i,int val) 25 { 26 ro(k,20,0)if(dis[i][k][0]+dis[i][k][1]<=val&&To[i][k]) 27 val-=(dis[i][k][0]+dis[i][k][1]), 28 tot[1]+=dis[i][k][1],tot[0]+=dis[i][k][0],i=To[i][k]; 29 if(des[i][1]&&Min[i][1]<=val)tot[1]+=Min[i][1]; 30 } 31 int main(){scanf("%d",&n);go(i,1,n)scanf("%d",&h[i]),Min[i][1]=Min[i][0]=inf; 32 ro(i,n,1) 33 { 34 box.insert((info){h[i],i}); 35 I=box.find((info){h[i],i});++I; 36 if(I!=box.end())consider(i,*I),++I,I!=box.end()?consider(i,*I),1:1,--I;--I; 37 if(I!=box.begin())--I,consider(i,*I),I!=box.begin()?--I,consider(i,*I),1:1; 38 } 39 40 go(i,1,n)To[i][0]=des[des[i][1]][0], 41 dis[i][0][1]=Min[i][1],dis[i][0][0]=Min[des[i][1]][0]; 42 43 go(k,1,20)go(i,1,n)To[i][k]=To[To[i][k-1]][k-1], 44 dis[i][k][1]=dis[i][k-1][1]+dis[To[i][k-1]][k-1][1], 45 dis[i][k][0]=dis[i][k-1][0]+dis[To[i][k-1]][k-1][0]; 46 47 scanf("%d",&x);double rate=inf;int pos=0;h[0]=-inf;go(i,1,n) 48 { 49 tot[0]=tot[1]=0;doubling(i,x);double tmp=tot[0]?1.0*tot[1]/tot[0]:inf; 50 if(tmp-rate<eps&&tmp-rate>-eps&&h[i]>h[pos])pos=i; 51 if(rate-tmp>eps)pos=i,rate=tmp; 52 } 53 54 printf("%d\n",pos);scanf("%d",&m);go(i,1,m) 55 { 56 scanf("%d%d",&s,&x); 57 tot[0]=tot[1]=0;doubling(s,x); 58 printf("%d %d\n",tot[1],tot[0]); 59 } 60 return 0; 61 }//Paul_Guderian?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
只許集中,不許分散。————海因茨·威廉·古德里安
?
轉(zhuǎn)載于:https://www.cnblogs.com/Paul-Guderian/p/6853123.html
總結(jié)
以上是生活随笔為你收集整理的【NOIP2012-开车旅行】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用eclipse修改web工程的访问路径
- 下一篇: 图论讲解(3)——最小生成树(基础)