關於瑞士輪(洛谷P1309)以及引申出來的種種問題

題目鏈接

原創鏈接


首先來看題(沒看題的看題去!),題面應該不難理解,就是每次相鄰分數的兩個人根據實力值進行比較,然後輸贏分治,不斷排序罷了。

“肯定要sort哇!每次更新分數,然後sort不就得了?”

其實本質上來說,是可以的,但是sort會爆炸——時間會爆炸。但是無論時間怎樣,那都是ccf的測試點有沒有卡tle的問題而已。但如果真從程序設計本身探討,sort無疑是一個很浪費的算法。

一、關於sort的浪費

首先讓我們想想,sort其實就是快速排序,而快速排序其實就是二分的思想(胡亂在中間立flag)。穩定的話O(nlogn)左右。但是仔細想想此題——每次需要更新的值,都是相鄰兩個人變化後的分數;而相鄰的分數,有些是不會改變位置的,而快速排序則是每次全部修改,必然會造成浪費。

二、關於歸併排序

然後考慮歸併排序: 歸併排序的思想就是合併兩個同序數組的線性方式——每次比較兩個有序數組指針指向的值,誰更小(大)則放到temp數組裏,然後刪掉進入temp的元素,指針++。

於是歸併排序的代碼就不難理解了:


void merge(int l,int r)
{

    if(l==r)return 0;

    int mid=(l+r)/2;

    merge(l,mid);

    merge(mid+1,r);

    int i=l,j=mid+1,p=l;

    while(i<=mid&&j<=r)

    {

        if(a[i]>a[j])temp[++p]=a[++i];

        else temp[++p]=a[++j];
    } 

    while(i<=mid)temp[++p]=a[++i];

    while(j<=r)temp[++p]=a[++j];

    for(int i=l;i<=r;i++)a[i]=temp[i];

} 


在歸併排序中,無非就是將“兩個有序數組”變成“一個被一分爲二的數組(也

是兩個)”——因爲不斷二分後,剩下的單個元素必定有序,所以合併相鄰相

鄰元素並使之有序,之後產生兩個有序區間等價於合併兩個有序數組。但

此處仍有值得注意的地方,就是由於兩個數組的大小關係具有不確定性,在第

一個while結束後兩個原數組中有剩餘的元素未參與排序,所以需要再加兩個

while來處理剩餘元素(此時一定是隻會執行其中的一個while,原因不言自

明)。最後,一定要把過程數組temp覆蓋原數組a的值,保證每次傳遞

到上一級區間(大區間)的數值都有序。

穩定複雜度:O(nlogn)

三、關於爲何引進歸併排序

大家可以發現,歸併排序每次的操作只針對相鄰區間,或者說合並時是對相鄰幾個區間的操作,所以這符合只需要修改相鄰幾個分數的排布狀況的題意。即使和快排的複雜度相同,但是省掉了冗雜無用的操作,是一個極大的改良。

最後,附ac代碼:


#include<iostream> 
#include<algorithm>    
using namespace std;  
int n,r,q;  
int a[200100],win[200100],lose[200100];  
int s[200100],w[200100];   
bool cmp(int x,int y)  
{  
  if(s[x]==s[y])   return x<y;
  return s[x]>s[y];
}  
void merge()  
{  
  int i,j;  
  i=j=1,a[0]=0;  
  while(i<=win[0] && j<=lose[0])  
    if(cmp(win[i],lose[j]))  
      a[++a[0]]=win[i++];  
    else   
      a[++a[0]]=lose[j++];  
  while(i<=win[0])a[++a[0]]=win[i++];  
  while(j<=lose[0])a[++a[0]]=lose[j++];          
}  
int main()  
{  
  cin>>n>>r>>q;n*=2;  
  for(int i=1;i<=n;i++)a[i]=i;  
  for(int i=1;i<=n;i++)cin>>s[i];  
  for(int i=1;i<=n;i++)cin>>w[i];  
  sort(a+1,a+n+1,cmp);  
  for(int i=1;i<=r;i++)  
    {  
      win[0]=lose[0]=0;  
      for(int j=1;j<=n;j+=2)  
        if(w[a[j]]>w[a[j+1]])  
          {  
            s[a[j]]++;  
            win[++win[0]]=a[j];  
            lose[++lose[0]]=a[j+1];  
          }  
        else  
          {  
            s[a[j+1]]++;  
            win[++win[0]]=a[j+1];  
            lose[++lose[0]]=a[j];  
          }    
      merge();          
    }  
  cout<<a[q];
  return 0;  
}  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章