1. 程式人生 > >Contest1592 - 2018-2019賽季多校聯合新生訓練賽第二場(部分題解)

Contest1592 - 2018-2019賽季多校聯合新生訓練賽第二場(部分題解)

D 10248 修建高樓

 

D 傳送門

題幹

題目描述
    C 市有一條東西走向的“市河”。C 市的市長打算在“市河”的其中一條岸邊自東往西的 n 個位置(可以將這 n 個位置看成在一條直線上,且位置不會重疊)依次建造高樓。  
    C 市的設計部門設計了 T 個方案供市長挑選(方案編號為 1 到 T)。每個方案都提供了建造的每幢高樓的高度,自東向西依次為 h1,h2,h3,…,hn-1,hn。每幢樓房的高度在 1 到 n 之間(包括 1 和 n),且各不相同。  
    市長在挑選設計方案時,喜歡 n 幢高樓中任意 3 幢(包括不連續的 3
幢)有一定的“梯度美”。所謂“梯度美”是指這 3 幢高樓滿足: 第j幢的高度hj-第i幢的高度hi=第k幢的高度hk-第j幢的高度hj(1≤i<j<k≤n) 市長喜歡方案中這種“梯度美”現象越多越好。請程式設計幫市長挑選一下設計方案吧。 輸入 T+1 行。 第一行兩個整數 T 和 n,分別表示設計部門提供的方案總數和打算建造的高樓數。 接下來每一行表示一種方案。第 i+1 行表示第 i 種方案,每行 n 個整數,依次表示每幢高樓打算建造的高度。 輸出 輸出共 1 行。 包含兩個整數,第一整數為出現“梯度美”次數最多的方案,第二個整數為對應方案“梯度美”出現的次數。如果出現“梯度美”次數最多的方案有多個,輸出方案編號較小的方案。 樣例輸入
2 5 3 1 2 4 5 3 1 2 5 4 樣例輸出 1 1 提示 輸入中共有2個方案,打算建造5幢高樓。 第一個方案每幢高樓高度依次為3,1245,其中第1幢,第4幢和第5幢高度出現“梯度美”(345),這3幢高樓的後一幢比前一幢依次高1。 第二個方案每幢高樓高度依次為3,1254,沒有出現“梯度美”。 (1≤T≤50,且 3≤n≤2000
View Code

題解:

  考察知識點:模擬優化

    這道題,昨天下午考完試一直在看,看了好久好久,一直在找nlogn複雜度的演算法(為什麼要找nlogn複雜度的演算法呢?因為我感覺,如果t=50,n=2000,那麼

    就有1e6個樓房,而1e5的資料範圍需要nlogn的時間複雜度,然後,就一直找不到在哪可以logn,嗚嗚嗚~~~~)

    實屬無奈,然後,就找老師要了一份標程,標程如下:

 1 #include<stdio.h>
 2 #include<string.h>
 3 int main()
 4 {
 5     int t,n,a[2005],ans=-1,ans1=0,b[10005]= {0},c;
 6     scanf("%d%d",&t,&n);
 7     for(int q=0; q<t; q++)
 8     {
 9         memset(b,0,sizeof(b));
10         c=0;
11         for(int i=0; i<n; i++)
12         {
13             scanf("%d",&a[i]);
14             b[a[i]+4000]=i;
15         }
16         for(int i=0;i<n-2;i++)
17             for(int j=i;j<n-1;j++)
18             if(b[2*a[j]-a[i]+4000]>j)
19             c++;
20         if(c>ans)
21             ans1=q,ans=c;
22     }
23     printf("%d %d",ans1+1,ans);
24     return 0;
25 }
View Code

    照著標程理解了一下,具體做法是列舉i,j樓的高度,判斷是否存在滿足條件的k樓,是個O(n^2)的複雜度,很納悶,這怎麼能過呢?????

  其實,在找老師要標程前,在ACM的群裡問了一下,一個初三大佬,五分鐘敲出的這道題,一發AC,這,這也太厲害了吧%%%%%%%

  差距太大了

  之所以要他寫程式碼,是因為,標程裡將memset()放到了迴圈內,然後,他說,將memset()放迴圈裡很不好,有時候會因此而超時,然後,沒有然後了。。。

  巨巨程式碼:

 1 #include <cstdio>
 2 using namespace std;
 3 
 4 int hi[2005];
 5 int pos[4005];
 6 
 7 int main()
 8 {
 9     int t,n;
10     scanf("%d%d",&t,&n);
11 
12     int ans = 0;
13     int ansp = 1;
14     for(int l=1; l<=t; ++l)
15     {
16         for(int i=1; i<=n; ++i)
17             scanf("%d",hi+i);
18 
19         for(int i=1; i<=n; ++i)
20             pos[hi[i]<<1] = i;
21 
22         int curans = 0;
23         for(int i=1;i <= n-2;++i)
24             for(int j=i+2;j <= n;++j)
25                 if(i<pos[hi[i]+hi[j]] && pos[hi[i]+hi[j]]<j)
26                     ++curans;
27 
28         if(curans>ans)
29         {
30             ans = curans;
31             ansp =  l;
32         }
33     }
34     printf("%d %d\n",ansp,ans);
35 
36     return 0;
37 }
View Code

  具體思路是,列舉i,k,判斷有沒有滿足條件的 j 。

  偷偷把他的程式碼改成我的風格,哈哈哈

AC程式碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int maxn=2000+10;
 5 
 6 int t,n;
 7 int h[maxn];
 8 int pos[2*maxn];
 9 
10 int Solve()
11 {
12     for(int i=1;i <= n;++i)
13         pos[h[i]<<1]=i;//2*h[j]的位置
14     int res=0;
15     for(int i=1;i <= n-2;i++)
16         for(int k=i+2;k <= n;++k)
17             if(pos[h[i]+h[k]] > i && pos[h[i]+h[k]] < k)//判斷(i,k)之間有沒有h[i]+h[k]
18                 res++;
19     return res;
20 }
21 int main()
22 {
23     scanf("%d%d",&t,&n);
24     int resTot=0,resPos=1;
25     for(int kase=1;kase <= t;++kase)
26     {
27         for(int i=1;i <= n;++i)
28             scanf("%d",h+i);
29         int res=Solve();
30         if(resTot < res)
31             resTot=res,resPos=kase;
32     }
33     printf("%d %d\n",resPos,resTot);
34 
35     return 0;
36 }
View Code