算法競賽專題講座結課做業

題目一node

算法分類:並查集,DFS,Tarjan算法c++

原題:算法

How far away ?

Time Limit: 2000/1000 MS (Java/Others)數組

Memory Limit: 32768/32768 K (Java/Others)less

 

Description

There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.

Input

First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting  house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.

Output

For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
 

Sample Input

2 3 2 1 2 10 3 1 15 1 2 2 3 2 2 1 2 100 1 2 2 1

Sample Output

10 25 100 100
 
題目大意:
   現有n個村莊,村莊之間經過公路相連,而且任意兩個村莊之間只有一條最簡路徑,也就是不重複通過其中的任意一條路,每條路有其對應的長度,現給出村莊個數,已經任意兩兩村莊之間存在的公路及公路的長度,須要你求出任意兩個村莊之間的距離
 
題目實質:
    一個n個節點帶邊權值的無環圖,求圖中任意兩個節點的最近公共祖先
 
題解:
   首先先來看一看我第一遍本身作的時候TLE的作法。當時由於歷來沒有接觸過LCA的相關問題,因此採起了一種很是暴力的作法。
    肯定了這條題目是一個n個節點的無環圖以後,個人想法是肯定任意一個點爲根節點,而後經過並查集而且按秩合併,將每一個節點所在的高度,都求出來。如肯定1這個點爲根節點,那麼這個點的高度爲0,接着全部與1直接相連的節點的高度爲1,依次往下類推。開一個數組,把每一條邊的長度都保存在高度的數值較大的那個點上。
    緊接着,須要查詢兩個節點之間的距離,若是兩個節點在同一條路徑上,那麼只要將從一個節點到另外一節點之間的全部邊長相加便可。若是不在同一條路徑上,那麼只要將分別將兩個節點到最近公共祖先的距離求出再相加便可。
    那麼,首先判斷兩個點是否在同一個高度,若是不在的話,將高度較低的點向上移動直到高度相等爲止,在過程當中順便加上通過邊的邊長。高度相等之後,判斷兩點是否重合,若是重合就結束,若是不重合,就將兩個點每次同時向上移動一個高度,直到兩個點重合爲止。
TLE的代碼:
  
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+5;
 4 int s[maxn],height[maxn],val[maxn];
 5 int n,m;
 6 inline int read(){
 7     register int x=0,f=1;char c=getchar();
 8     while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
 9     while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
10     return x*f;
11 }
12 int find_set(int x){                   
13     return x==s[x]?x:find_set(s[x]);
14 }
15 void init_set(){   
16    for(int i = 1; i <= n; i++){
17         s[i] = i;
18         height[i]=0;       
19    }
20 }
21 void union_set(int x1, int y1,int l){             //將聯通的兩個節點按秩合併 
22     int x = find_set(x1);
23     int y = find_set(y1);
24     if (height[x] == height[y]) {
25         height[x] = height[x] + 1; 
26         s[y1] = x1;
27         val[y1]=l;       
28     }
29     else{
30         if (height[x]<height[y]) s[x1]=y1,val[x1]=l;
31         else  s[y1] = x1,val[y1]=l;
32     }
33     if(s[y1]==x1)    {
34         int t=y1;
35         while(t!=s[t]){
36             height[s[t]]=max(height[s[t]],height[t]+1);
37             t=s[t];
38         }
39     }
40     else{
41         int t=x1;
42         while(t!=s[t]){
43             height[s[t]]=max(height[s[t]],height[t]+1);
44             t=s[t];
45         }
46     }
47 }
48 int main(){
49     int t;
50     t=read();
51     while(t--){
52     n=read();m=read();
53     n-=1;
54     init_set();
55     while(n--){
56         int x,y,v;
57         x=read();
58         y=read();
59         v=read();
60         union_set(x,y,v);
61     }
62     while(m--){
63         int x,y,k;
64         x=read();y=read();
65         long long cnt=0;
66         if(height[y]>height[x]){                    //將不在同一高度的兩個節點移動至同一個高度 
67             while(height[x]!=height[y]){
68                 cnt+=val[x];
69                 x=s[x];
70             }
71         }
72         else if(height[y]<height[x]){
73             while(height[y]!=height[x]){
74                 cnt+=val[y];
75                 y=s[y];
76             }
77         }
78         else{}
79         if(x==y){                                        
80             printf("%lld\n",cnt);continue;            //若是兩點已經重合,輸出結果 
81         }
82         else{                                        //將兩點同時上移,直至兩點重合 
83             while(x!=y){
84                 cnt+=val[x]+val[y];
85                 x=s[x];
86                 y=s[y];
87             }
88         }
89         printf("%lld\n",cnt);
90         }
91     }
92     return 0;
93 }

      雖然這道題目只有4e4的數據,而且給了兩秒的時間,可是這樣暴力的作法仍是超時了。本身作了很多改進仍是沒有任何效果。因此這道題目得推翻一開始的作法從新來。函數

   想了很久沒有思路,後來經過幾篇博客,瞭解到了Tarjan算法。ui

   先來講一說Tarjan算法:this

      Tarjan算法其實是一種離線算法。所謂離線,就是經過一次遍歷一次性將全部詢問結果進行計算並進行保存。最後不須要再回到圖中計算直接輸出結果。其時間複雜度爲O(n+q);spa

  n爲全部節點的總個數,q爲全部詢問的個數。3d

        Tarjin算法的基本流程大概是:

  1.任選一個節點爲根節點,以這個節點爲起點

 

  2.遍歷該點u全部子節點v,並標記這些子節點v已被訪問過。

  3.如果v還有子節點,返回2,不然下一步。

  4.將v的祖先記爲u。

  5.尋找與當前點u有詢問關係的點v。如果v已經被訪問過了,則能夠確認u和v的最近公共祖先爲v被合併到的父親節點。若是v 沒有被訪問,則繼續下一步操做

 

  其實畫一個圖本身來手動模擬會顯得更加直觀

  如今有這樣一個圖:                                                                                                                           

 

   

初始化:    

fa[1]=1,vis[1]=false              如今有兩組點須要詢問,要求這兩個點的最近公共祖先

fa[2]=2,vis[2]=false                分別爲點2和點4,點8與點5

fa[3]=3,vis[3]=false

fa[4]=4,vis[4]=false

fa[5]=5,vis[5]=false

fa[6]=6,vis[6]=false

fa[7]=7,vis[7]=false

fa[8]=8,vis[8]=false

 
   下面開始模擬的過程,首先以點1爲根節點,發現點1有兩個子節點,點2和點3,先對點2進行操做,
  發現點2有兩個子節點,點4和點5。發現點4有子節點點8,再對點8進行操做,點8沒有子節點,查看詢問中
  與8有關係的點爲點5,可是vis[5]=false,點5還未搜索到,因而跳過,vis[8]=true。將點8合併到它的父節點點4上
 
        vis[8]=false --> vis[5]=false  -->vis[8]=true -->fa[8]=4;
        接着再對8的父節點4進行操做,查看詢問中與4有關係的點爲點2,可是vis[2]=false,點2還未搜索到,因而跳過,
  vis[4]=true。將點4合併到它的父節點點2上
   vis[4]=false  -->vis[2]=false  -->vis[4]=true  -->fa[4]=2;
        接下來,再對點4的父節點2進行操做,查看詢問中與點2 有關係的節點爲點4,而且vis[4]=true,因此節點2和
  節點4的最近公共祖先爲find(4)=2,答案爲2,而且更新vis[2]=true;
  int find(int i) {return fa[i]==i?i:find(fa[i])}
  vis[2]=false -->vis[4]=true  --> ans=find(4)  -->vis[2]=true;
 
 
 
   
   下面繼續搜點2的另外一個子節點點5,發現點5沒有子節點,因而查看詢問中與點5有關係的節點爲點8,發現vis[8]=true,
  因而答案爲find(8)=2,因而點5和點8的最近公共祖先爲點2。更新vis(5)=true;
   fa[8]=4  -->fa[4]=2 -->fa[2]=2  -->ans=2
    vis[5]=false  -->vis[8]=true  -->ans=find(8)=2 -->vis[5]=true;
 
依次類推,咱們能夠經過一次DFS,按照分支的順序依次獲得全部詢問的答案。知道搜索完根節點點1的全部分支以後,咱們即可以退出DFS。
 
下面上代碼:
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=40000+5;
 4 struct Edge{
 5     int cnt,x[N],y[N],z[N],nxt[N],fst[N];
 6     void set(){
 7         cnt=0;
 8         memset(x,0,sizeof x);
 9         memset(y,0,sizeof y);
10         memset(z,0,sizeof z);
11         memset(nxt,0,sizeof nxt);
12         memset(fst,0,sizeof fst);
13     }
14     void add(int a,int b,int c){
15         x[++cnt]=a;
16         y[cnt]=b;
17         z[cnt]=c;
18         nxt[cnt]=fst[a];
19         fst[a]=cnt;
20     }
21 }e,q;
22 int T,n,m,from,to,dist,in[N],rt,dis[N],fa[N],ans[N];
23 bool vis[N];
24 void dfs(int rt){
25     for (int i=e.fst[rt];i;i=e.nxt[i]){
26         dis[e.y[i]]=dis[rt]+e.z[i];
27         dfs(e.y[i]);
28     }
29 }
30 int getf(int k){
31     return fa[k]==k?k:fa[k]=getf(fa[k]);
32 }
33 void LCA(int rt){
34     for (int i=e.fst[rt];i;i=e.nxt[i]){
35         LCA(e.y[i]);
36         fa[getf(e.y[i])]=rt;
37     }
38     vis[rt]=1;
39     for (int i=q.fst[rt];i;i=q.nxt[i])
40         if (vis[q.y[i]]&&!ans[q.z[i]])
41             ans[q.z[i]]=dis[q.y[i]]+dis[rt]-2*dis[getf(q.y[i])];
42 }
43 int main(){
44     scanf("%d",&T);
45     while (T--){
46         q.set(),e.set();
47         memset(in,0,sizeof in);
48         memset(vis,0,sizeof vis);
49         memset(ans,0,sizeof ans);
50         scanf("%d%d",&n,&m);
51         for (int i=1;i<n;i++)
52             scanf("%d%d%d",&from,&to,&dist),e.add(from,to,dist),in[to]++;
53         for (int i=1;i<=m;i++)
54             scanf("%d%d",&from,&to),q.add(from,to,i),q.add(to,from,i);
55         rt=0;
56         for (int i=1;i<=n&&rt==0;i++)
57             if (in[i]==0)
58                 rt=i;
59         dis[rt]=0;
60         dfs(rt);
61         for (int i=1;i<=n;i++)
62             fa[i]=i;
63         LCA(rt);
64         for (int i=1;i<=m;i++)
65             printf("%d\n",ans[i]);
66     }
67     return 0;
68 }

 

 

 

題目二

算法分類:遞推DP,組合數

原題:HDU4489

 

The King’s Ups and Downs

Time Limit: 2000/1000 MS (Java/Others)

Memory Limit: 32768/32768 K (Java/Others)

 

Description

The king has guards of all   different heights. Rather than line them up in increasing or  decreasing height order, he wants to line them up so each guard   is either shorter than the guards next to him or taller than the guards next to him (so the heights go up and  down along the line). For example, seven guards of heights 160, 162, 164, 166, 168, 170   and   172 cm. could be arranged as:


or perhaps:


The king wants to know how many guards he needs so he can have a   different up and down order at each changing of the guard for rest of his reign. To be able to do this, he needs to know for a given number of guards, n, how many  different up and  down orders there are:

For example, if there are four guards: 1, 2, 3,4 can be arrange   as:

1324, 2143, 3142, 2314, 3412, 4231, 4132, 2413, 3241, 1423

For this problem, you will write a program that takes as input a positive integer n, the number of guards and returns the number of up and down orders for n guards of   differing heights.

 

Input

The first line of input contains a single integer P, (1 <= P <= 1000), which is the number of  data sets that follow.  Each  data set consists of single line of input containing two integers.  The first integer, D is the   data set number.  The second  integer, n (1 <= n <= 20), is the number of guards of   differing heights.

 

Output

For each   data set there is one line of output.  It contains the   data set number (D) followed  by a single space, followed by the number of up and down orders for the n guards.

 

Sample Input

4
1 1
2 3
3 4
4 20

 

Sample Output

1 1
2 4
3 10
4 740742376475050
 
題目大意:
  國王想要給他的士兵排隊,如今有n個士兵,身高各不相同,國王想要使他的士兵按照「高矮高矮高矮......」或者「矮高矮高矮高......」的順序排成一隊,如今想要求出對於n個士兵,存在多少種不一樣的排列方法。n最大爲20。
 
題解:
   這道題目n最大隻有20,但當n達到20的時候,答案已是一個很大很大的數了,顯然是一個指數級的增加,暴力的作法確定跑不出來。想到使用動態規劃。
   如今咱們先給n個士兵按照身高從矮到高排序,按照從矮到高的順序依次插入每一個士兵。對於每次插入的士兵i,他的身高必定高於已經在隊列中的i-1個士兵中的任意一個。對於
士兵i放入的每個位置,在他前面的士兵必定是以「高矮」結尾,而在他後面的士兵則必定是「矮高」開頭,因此若是將士兵i放在第j個位置,那麼對於的排列方式便爲前面的j個士兵以「高矮」結尾的方法數,乘之後面的(i-1-j)個士兵以「矮高」開頭的方法數,因爲j個士兵究竟是哪j個士兵並無肯定,因此還要乘上從全部的i-1個士兵中選取j個士兵的方法數Ci-1j,這樣將j從0到i-1的全部狀況相加,即可以獲得排列i個士兵的總方法數。
 
  對於組合數的計算,咱們只須要用一個最基礎的遞推式便可,即Cij=Ci-1j-1+ci-1j。代碼以下。
1 c[1][0]=c[1][1]=1;
2     for(int i=2;i<=20;i++){
3         c[i][0]=c[i][i]=1;
4         for(int j=1;j<i;j++){
5             c[i][j]=c[i-1][j-1]+c[i-1][j];
6         }
7     }

 

  咱們能夠用兩個dp數組,dp1和dp2分別表示以「高矮」結尾的方法數和以「矮高」開頭的方法數。因爲對於n個士兵排列的總方法數,以「高矮」和「矮高」開頭的方法數各佔總數的一半,同理以「高矮」結尾的方法數也同樣。因此dp1[n]和dp2[n]都等於ans[n]的一半。

  遞推方程爲:

    ans[i]=dp1[j]*dp2[i-1-j]*c[i-1][j]  (0<=j<i)

  固然若是隻用一個dp數組也是同樣,

    dp[i]=(dp[j]/2)*(dp[i-1-j]/2)*c[i-1][j]  (0<=j<i)

  這裏n最大隻有20,因此直接一次性打表把全部答案存下來,避免後面重複計算。注意答案的數值可能很大,因此要用long long。

上代碼:

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 ll c[22][22];
 5 ll dp1[25],dp2[25];
 6 #define scan(i) scanf("%d",&i)
 7 int main(){
 8     int N;scan(N);
 9     c[1][0]=c[1][1]=1;
10     for(int i=2;i<=20;i++){
11         c[i][0]=c[i][i]=1;
12         for(int j=1;j<i;j++){
13             c[i][j]=c[i-1][j-1]+c[i-1][j];
14         }
15     }
16     dp1[1]=dp1[0]=dp2[0]=dp2[1]=1;
17     for(int i=2;i<=20;i++){
18         ll t=0;
19         for(int j=0;j<i;j++){
20             t+=dp1[j]*dp2[i-1-j]*c[i-1][j];
21         }
22         dp1[i]=dp2[i]=t/2;
23     }
24     while(N--){
25         int x,y;
26         scanf("%d%d",&x,&y);
27         if(y==1){
28             printf("%d 1\n",x);
29             continue;
30         }
31         ll ans=dp1[y]*2;
32         printf("%d %lld\n",x,ans);
33     }
34     return 0; 
35 }

 

 

 

題目三

題目來源:CF#552(div3)

G題:數論

G. Minimum Possible LCM

time limit per test
4 seconds
memory limit per test
1024 megabytes
input
standard input
output
standard output

 

You are given an array aa consisting of nn integers a1,a2,,ana1,a2,…,an .

Your problem is to find such pair of indices i,ji,j (1i<jn1≤i<j≤n ) that lcm(ai,aj)lcm(ai,aj) is minimum possible.

lcm(x,y)lcm(x,y) is the least common multiple of xx and yy (minimum positive number such that both xx and yy are divisors of this number).

 

Input

The first line of the input contains one integer nn (2n1062≤n≤106 ) — the number of elements in aa .

The second line of the input contains nn integers a1,a2,,ana1,a2,…,an (1ai1071≤ai≤107 ), where aiai is the ii -th element of aa .

 

Output

Print two integers ii and jj (1i<jn1≤i<j≤n ) such that the value of lcm(ai,aj)lcm(ai,aj) is minimum among all valid pairs i,ji,j . If there are multiple answers, you can print any.

 

Examples
Input
 
5
2 4 8 3 6
Output
 
1 2
Input
 
5
5 2 11 3 7
Output
 
2 4
Input
 
6
2 5 10 1 10 2
Output
 
1 4

 

題意:

    給出n個數,要求輸出最小公倍數最大的兩個數的下標

知識補充:GCD和LCM:

    GCD即最大公約數,求最大公約數有多種方法,常見的有質因數分解法、短除法、展轉相除法、更相減損法。

     algorithm庫裏有函數__gcd()

     歐幾里得(展轉相除法):

    

 1 int gcd(int a,int b)
 2 
 3 {
 4 
 5     if(b==0) return a;
 6 
 7     return gcd(b,a%b);
 8 
 9 }
10 
11 
12 
13 
14 int gcd(int x,int y){return y==0?x:GCD(x%y)}

 

 

    更相減損術:

 1 while(!(a%2) && !(b%2))
 2 {
 3         a = a/2;
 4         b = b/2;
 5 }
 6 while(a != b)
 7 {
 8         if(a>b){
 9             a = a-b;
10         }else{
11             b = b-a;
12         }
13 }

 

    stein算法:Stein算法只有整數的移位和加減法。原理以下:

  • 若a和b都是偶數,則記錄下公約數2,而後都除2(即右移1位);
  • 若其中一個數是偶數,則偶數除2,由於此時2不多是這兩個數的公約數了
  • 若兩個都是奇數,則a = |a-b|,b = min(a,b),由於若d是a和b的公約數,那麼d也是|a-b|和min(a,b)的公約數。
 1 int SteinGCD(int a, int b) {
 2     if (a < b) { int t = a; a = b; b = t; }
 3     if (b == 0) return a;
 4     if ((a & 1) == 0 && (b & 1) == 0)
 5         return SteinGCD(a >> 1, b >> 1) << 1;
 6     else if ((a & 1) == 0 && (b & 1) != 0)
 7         return SteinGCD(a >> 1, b);
 8     else if ((a & 1) != 0 && (b & 1) == 0)
 9         return SteinGCD(a, b >> 1);
10     else
11         return SteinGCD(a - b, b);
12 }

 

    LCM即最小公倍數

    有以下公式 LCM(a,b)*GCD(a,b)=a*b;經過分解分解質因數能夠很容易的證實。

 1 int GCD(int x,int y){
 2 
 3     return !y?x:GCD(y,x%y);
 4 
 5 }
 6 
 7 int LCM(int x,int y){
 8 
 9     return x*y/GCD(x,y);
10 
11 }

 

題解:   

    首先,將每一個出現的位置記在pos[]數組裏,考慮到,存在lcm(x,x)==x的情形,在讀入的時候特判一下再考慮到,lcm(x,y)==x*y/gcd(x,y)枚舉i==gcd(x,y),對於每一個i,找到其倍數裏出現的最小的兩個x、y,這個是埃篩的操做,j+=i的時候會遍歷到i的倍數,取到兩個就break就好,畢竟更大的x、y,對於固定下來的gcd來說,答案不會更優。
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=1e7+10;
 5 int n,v;
 6 int pos[maxn];
 7 ll ans;
 8 int ai,aj; 
 9 int main(){
10     scanf("%d",&n);
11     ans=1e18;
12     for(int i=1;i<=n;i++){
13         scanf("%d",&v);
14         if(pos[v]&&ans>v)
15         ans=v,ai=pos[v],aj=i;
16         pos[v]=i;
17     }
18     for(ll i=1;i<maxn;i++)
19     {
20         ll first=-1;
21         for(ll j=i;j<maxn;j+=i)
22         {
23             if(pos[j])
24             {
25                 if(first==-1)first=j;
26                 else 
27                 {
28                    if(ans>first*j/i)
29                    ans=first*j/i,ai=pos[first],aj=pos[j];
30                    break;
31                 }
32             }
33         }
34     }
35     if(ai>aj){
36         int temp=ai;
37         ai=aj;
38         aj=temp;
39     }
40     printf("%d %d\n",ai,aj);
41     return 0;
42 }

 

E題:

    

E. Two Teams
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

There are nn students standing in a row. Two coaches are forming two teams — the first coach chooses the first team and the second coach chooses the second team.

The ii -th student has integer programming skill aiai . All programming skills are distinct and between 11 and nn , inclusive.

Firstly, the first coach will choose the student with maximum programming skill among all students not taken into any team, and kk closest students to the left of him and kk closest students to the right of him (if there are less than kk students to the left or to the right, all of them will be chosen). All students that are chosen leave the row and join the first team. Secondly, the second coach will make the same move (but all students chosen by him join the second team). Then again the first coach will make such move, and so on. This repeats until the row becomes empty (i. e. the process ends when each student becomes to some team).

Your problem is to determine which students will be taken into the first team and which students will be taken into the second team.

Input

The first line of the input contains two integers nn and kk (1kn21051≤k≤n≤2⋅105 ) — the number of students and the value determining the range of chosen students during each move, respectively.

The second line of the input contains nn integers a1,a2,,ana1,a2,…,an (1ain1≤ai≤n ), where aiai is the programming skill of the ii -th student. It is guaranteed that all programming skills are distinct.

Output

Print a string of nn characters; ii -th character should be 1 if ii -th student joins the first team, or 2 otherwise.

Examples
Input
5 2
2 4 5 3 1
Output
11111
Input
5 1
2 1 3 5 4
Output
22111
Input
Copy
7 1
7 2 1 3 5 4 6
Output
1121122
Input
5 1
2 4 5 3 1
Output
21112
Note

In the first example the first coach chooses the student on a position 33 , and the row becomes empty (all students join the first team).

In the second example the first coach chooses the student on position 44 , and the row becomes [2,1][2,1] (students with programming skills [3,4,5][3,4,5] join the first team). Then the second coach chooses the student on position 11 , and the row becomes empty (and students with programming skills [1,2][1,2] join the second team).

In the third example the first coach chooses the student on position 11 , and the row becomes [1,3,5,4,6][1,3,5,4,6] (students with programming skills [2,7][2,7] join the first team). Then the second coach chooses the student on position 55 , and the row becomes [1,3,5][1,3,5] (students with programming skills [4,6][4,6] join the second team). Then the first coach chooses the student on position 33 , and the row becomes [1][1] (students with programming skills [3,5][3,5] join the first team). And then the second coach chooses the remaining student (and the student with programming skill 11 joins the second team).

In the fourth example the first coach chooses the student on position 33 , and the row becomes [2,1][2,1] (students with programming skills [3,4,5][3,4,5] join the first team). Then the second coach chooses the student on position 11 , and the row becomes empty (and students with programming skills [1,2][1,2] join the second team).

 

題意:

     現有n我的排成一隊,兩個教練輪流從隊中挑選最高的人以及最高的人左邊k我的和右邊k我的,如今須要你求出每一個人分別被哪個教練挑選走。

題解:

     這條題目其實就是一條模擬題,惟一要說的就是如何快速找到當前隊中存在的最大值,以及最大值所對應的位置。

    用一個check數組保存每個值的狀態,這樣在尋找最大值的時候,能夠從n開始,若是這個值已經被用過了,就減一再判斷,直到找到當前未用的最大值。而後用position數組保存每個值的下標,方便索引。

代碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=2e5+5;
 4 int n,k,x;
 5 struct node{
 6     int val;
 7     int id;
 8     node *pre;
 9     node *next;
10 };
11 node p[maxn];
12 int ans[maxn];
13 int position[maxn];          
14 bool check[maxn];            
15 inline void build(){
16     for(int i=1;i<=n;i++){
17         scanf("%d",&x);
18         p[i].val=x;
19         p[i].id=i;
20         p[i].pre=&p[i-1];p[i].next=&p[i+1];
21         if(i==1)p[i].pre=NULL;
22         if(i==n)p[i].next=NULL;
23         position[x]=i;
24     }
25     return;
26 }
27 int main(){
28     scanf("%d%d",&n,&k);
29     build();
30     int ma=n;
31     int an=1;
32     while(ma!=0){
33         check[ma]=true;
34         ans[position[ma]]=an;
35         int po=position[ma];
36         int k1=k;int k2=k;
37         int p1=po,p2=po;
38         while(k1--){
39             if(p[po].next==NULL)break;
40             int v=p[po].next->val; 
41             check[v]=true;
42             ans[position[v]]=an;
43             p1=p[po].next->id;
44             p[po].next=p[po].next->next;
45         }
46         while(k2--){
47             if(p[po].pre==NULL)break;
48             int v=p[po].pre->val; 
49             check[v]=true;
50             ans[position[v]]=an;
51             p2=p[po].pre->id;
52             p[po].pre=p[po].pre->pre;    
53         }
54         if(p[p1].next!=NULL)p1=p[p1].next->id;
55         else p1=n+1;
56         if(p[p2].pre!=NULL)p2=p[p2].pre->id;
57         else p2=0;
58         if(p1<=n&&p2>=1){
59             p[p1].pre=&p[p2];
60             p[p2].next=&p[p1];
61         }
62         if(p1<=n&&p2<1)p[p1].pre=NULL;
63         if(p1>n&&p2>=1)p[p2].next=NULL;
64         if(an==1)an=2;else an=1;
65         while(check[ma])ma--;
66     }
67     for(int i=1;i<=n;i++)
68         printf("%d",ans[i]);
69     return 0;
70 }
相關文章
相關標籤/搜索