申請SAE

如果您發現本博客的外觀很難看,那是因為部分外觀文件被中國.國家.防火.牆屏.蔽所致!
請翻~牆!

我的Wordpress博客的地址: http://zhuyf.tk/
顯示具有 NOIP 標籤的文章。 顯示所有文章
顯示具有 NOIP 標籤的文章。 顯示所有文章

2012年2月20日 星期一

NOIP1995 普及組 編碼問題 code

【問題描述】
設有一個數組 A : ARRAY[0..N-1] OF INTEGER ;數組中存儲的元素為 0-N-1 之間的整數,且 A[I] ≠ A[J] ( 當 I ≠ J) 時。
例如: N=6 時,有:( 4 , 3 , 0 , 5 , 1 , 2 )
此時,數組 A 的編碼定義如下:
A[0] 的編碼為 0 :
A[I] 的編碼為:在 A[0] , A[1] ,…… A[I-1] 中比 A[I] 的值小的元素的個數( I=1 , 2 ,…… N-1 )
所以上面數組 A 的編碼為 :B=(0,0,0,3,1,2)
程序要求解決以下問題
① 給出數組 A 後,求出其編碼;
② 給出數組 A 的編碼後,求出 A 的原資料。

2011年12月17日 星期六

[二分查找]NOIP2011提高組:聰明的質檢員 qc 解題報告

【問題描述】
試題檢視:GoogleDocs
小 T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n 個礦石,從 1 到 n 逐一編號,每個礦石都有自己的重量 wi 以及價值 vi。檢驗礦產的流程是:
1、給定 m個區間[Li,Ri];
2、選出一個參數 W;
3、對於一個區間[Li,Ri],計算礦石在這個區間上的檢驗值 Yi : 




若這批礦產的檢驗結果與所給標準值 S 相差太多,就需要再去檢驗另一批礦產。小 T 不想費時間去檢驗另一批礦產,所以他想通過調整參數 W 的值,讓檢驗結果儘可能的靠近標準值 S,即使得 S-Y的絕對值最小。請你幫忙求出這個最小值。
【輸入】
輸入文件 qc.in。
第一行包含三個整數n,m,S,分別表示礦石的個數、區間的個數和標準值。
接下來的n 行,每行2 個整數,中間用空格隔開,第i+1 行表示i 號礦石的重量wi 和價值vi 。
接下來的m 行,表示區間,每行2 個整數,中間用空格隔開,第i+n+1 行表示區間[Li,Ri]的兩個端點Li 和Ri。注意:不同區間可能重合或相互重疊。
【輸出】
輸出文件名爲qc.out。
輸出只有一行,包含一個整數,表示所求的最小值。
【輸入輸出樣例】
qc.in
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
qc.out
10
【輸入輸出樣例說明】
當W 選4 的時候,三個區間上檢驗值分別爲20、5、0,這批礦產的檢驗結果爲25,此時與標準值S 相差最小爲10。
【數據範圍】
對於10%的數據,有1≤n,m≤10;
對於30%的數據,有1≤n,m≤500;
對於50%的數據,有1≤n,m≤5,000;
對於70%的數據,有1≤n,m≤10,000;
對於100%的數據,有1≤n,m≤200,000,0 < wi, vi≤10^6,0 < S≤10^12,1≤Li≤Ri≤n。

 【分析】
本題的正解是二分W,從0~MaxW+1二分。計算Y時要先預處理,把所有大於W的礦石記錄下來,然後再計算每個區間的值。

二分結束後,找出Min{Get(Right),Get(Left)}輸出即可。

【我的代碼】
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int N,M;
long long S;
const int MAXN=200001;
int W[MAXN];
int V[MAXN];
int L[MAXN];
int Sum[MAXN];
int R[MAXN];
long long TV[MAXN];
int TN[MAXN];
  
void init()
{
    Sum[0]=0;
    cin>>N>>M>>S;
    for (int i=1;i<=N;i++)
    {
        cin>>W[i]>>V[i];
        Sum[i-1]=W[i];
    }
    for (int i=1;i<=M;i++)
        cin>>L[i]>>R[i];
    sort(Sum,Sum+N);
    Sum[N]=Sum[N-1]+1;
}

long long Get(int w)
{
    long long res=0;
    TV[0]=0;
    TN[0]=0;
    for (int i=1;i<=N;i++)
    {
        if(W[i]>=w)
        {
            TV[i]=TV[i-1]+V[i];
            TN[i]=TN[i-1]+1;
            continue;
        }
        else
        {
            TV[i]=TV[i-1];
            TN[i]=TN[i-1];
        }
    }
  
    for (int i=1;i<=M;i++)
    {
        int tl=L[i];
        int tr=R[i];
        long long Temp=(TN[tr]-TN[tl-1])*(TV[tr]-TV[tl-1]);
        res+=Temp;
    }
  
    return res;
}

long long Abs(long long t)
{
    if(t>0)
        return t;
    else
        return -t;
}

void dichotomy()
{
    int Right;
    int Left;
    for (Left=0,Right=N;Left+1<Right;)
    {
        long long Temp=Get(Sum[(Right+Left)/2]);
        if(Temp>=S)
            Left=(Left+Right)/2;
        else
            Right=(Left+Right)/2;
    }
  
    long long TW1,TW2;
    TW1=Abs(Get(Sum[Right])-S);
    TW2=Abs(Get(Sum[Left])-S);
    if(TW2<TW1)
    {
        TW1=TW2;
    }
    cout<<TW1<<endl;
}  

int main()
{
    freopen("qc.in","r",stdin);
    freopen("qc.out","w",stdout);
    init();
    dichotomy();
    return 0;
}

2011年12月8日 星期四

NOIP2011提高組 Day2 計算係數 factor 解題報告

題目查看、下載

【題目分析】
數論,C(k,m)*a^n*b^m即可。C(k,m)表示組合數,可以使用楊輝三角遞推求出。
考察知識點:
1) 二項式定理
2) 組合數遞推/楊輝三角
3) 同餘定理


【我的代碼】

#include <cstdio>
#include <iostream>
#include <cstdlib>
using namespace std;
int A,B,K,N,M;
const int MOD=10007;
int YH[1020][1020];
void yanghui()
{
 for (int i=0;i<=K+1;i++)
  for (int j=1;j<=K+2;j++)
   YH[i][j]=0;
 for (int i=0;i<=K+1;i++)
  YH[i][1]=1;
 for (int i=1;i<=K;i++)
 {
  for (int j=2;j<=i+1;j++)
  {
   YH[i][j]=(YH[i-1][j-1]+YH[i-1][j])%MOD;
  }
 }  
 
 /*
 for (int i=0;i<=K;i++)
 {
  for (int j=1;j<=i+1;j++)
   printf("%d ",YH[i][j]);
  printf("\n");
 }
 */
}
 
int Get(int num,int index)
{
 int res=1;
 for (int i=1;i<=index;i++)
  res=(res*num)%MOD;
 return res;
}
 
int main()
{
 freopen("factor.in","r",stdin);
 freopen("factor.out","w",stdout);
 scanf("%d %d %d %d %d\n",&A,&B,&K,&N,&M);
 yanghui();
 int NM=Get(A%MOD,N);
 int MM=Get(B%MOD,M);
 int res=(NM*MM)%MOD;
 res=(res*YH[K][M+1])%MOD;
 //printf("%d %d\n",NM,MM);
 printf("%d\n",res);
 return 0;
}

2011年11月28日 星期一

NOIP2011提高組 選擇客棧 hotel 解題報告

【問題描述】 
麗江河邊有n 家很有特色的客棧,客棧按照其位置順序從1 到n 編號。每家客棧都按照某一種色調進行裝飾(總共k 種,用整數0 ~ k-1 表示),且每家客棧都設有一家咖啡店,每家咖啡店均有各自的最低消費。兩位遊客一起去麗江旅遊,他們喜歡相同的色調,又想嘗試兩個不同的客棧,因此決定分別住在色調相同的兩家客棧中。晚上,他們打算選擇一家咖啡店喝咖啡,要求咖啡店位於兩人住的兩家客棧之間(包括他們住的客棧),且咖啡店的最低消費不超過p。他們想知道總共有多少種選擇住宿的方案,保證晚上可以找到一家最低消費不超過p元的咖啡店小聚。

 【輸入】輸入文件hotel.in,共n+1 行。第一行三個整數n,k,p,每兩個整數之間用一個空格隔開,分別表示客棧的個數,色調的數目和能接受的最低消費的最高值;接下來的n 行,第i+1 行兩個整數,之間用一個空格隔開,分別表示i 號客棧的裝飾色調和i 號客棧的咖啡店的最低消費。

 【輸出】輸出文件名為hotel.out。輸出只有一行,一個整數,表示可選的住宿方案的總數。 

【輸入輸出樣例1】 
hotel.in 
5 2 3
 0 5 
1 3 
0 2 
1 4 
1 5 
hotel.out 

3 
【輸入輸出樣例說明】

客棧編號

① ② ③ ④ ⑤
色調 0 1 0 1 1
最低消費 5 3 2 4 5
2人要住同樣色調的客棧,所有可選的住宿方案包括:住客棧①③,②④,②⑤,④⑤,但是若選擇住4、5 號客棧的話,4、5 號客棧之間的咖啡店的最低消費是4,而兩人能承受的最低消費是3 元,所以不滿足要求。因此只有前3 種方案可選。 

【數據范圍】
 對於30%的數據,有n≤100; 
對於50%的數據,有n≤1,000; 
對於100%的數據,有2≤n≤200,000,0<k≤50,0≤p≤100, 0≤最低消費≤100。

【分析】
 記憶化枚舉,或者動態規劃。
可以邊讀入邊計算:
 Pre[i]:前i個客棧中,最後一個 消費小於P的客棧。
Sum[i,j]:前j個客棧中,第i種顔色的總數量。   
S:總方案數

定義ci,pi表示讀入時第i個客棧的顔色、價格。
Sum[ci,i]=Sum[ci,i-1]+1;
Sum[j,i]=Sum[j-1,i](j!=ci)
 
如果pi<=P,Pre[i]=Pre[i-1]+1,S+=Sum[ci][Pre[i]]-1。
如果pi>P,Pre[i]=Pre[i-1],S+=Sum[ci][Pre[i]]。


最後輸出S即可。沒有必要使用long long類型。

時間複雜度:O(NK)
空間複雜度:NK


【我的代碼】
C++语言: Codee#24285

01 /*
02 *Problem:NOIP2011-Senior-Hotel
03 *Date:2011.11.27
04 *Author:Yee-fan Zhu
05 */
06 #include <cstdio>
07 #include <cstdlib>
08 #include <iostream>
09 using namespace std;
10
11 const int MAXN=200002;
12 int Sum[52][MAXN];
13 int Pre[MAXN];
14 int S=0;
15 int N,P,K;
16
17 void init()
18 {
19     std::ios::sync_with_stdio(false);
20     scanf("%d %d %d\n",&N,&K,&P);
21    
22     for (int i=0;i<=N;i++)
23         Pre[i]=0;
24     for (int i=0;i<=N;i++)
25         for (int j=0;j<=K;j++)
26             Sum[j][i]=0;
27    
28     int c,p;
29     for (int i=1;i<=N;i++)
30     {
31         scanf("%d %d\n",&c,&p);
32         Sum[c][i]=Sum[c][i-1]+1;
33        
34         for (int j=0;j<K;j++)
35             if(j!=c)
36                 Sum[j][i]=Sum[j][i-1];
37        
38         if(p<=P)
39         {
40             Pre[i]=i;
41             S+=Sum[c][Pre[i]]-1;
42         }
43         else
44         {
45             Pre[i]=Pre[i-1];
46             S+=Sum[c][Pre[i]];
47         }
48     }
49     printf("%d\n",S);
50 }
51
52 int main()
53 {
54     freopen("hotel.in","r",stdin);
55     freopen("hotel.out","w",stdout);
56     init();
57     return 0;
58 }


【評測結果】
正在连接评测机...

已连接到评测机
GRID 1
名称 Flitty
系统版本 1.00
备注 COGS 1号评测机 Flitty
正在编译...
编译成功

测试点 结果 得分 运行时间 内存使用 退出代码
1 正确 10 0.000 s 41681 KB 0
2 正确 10 0.000 s 41681 KB 0
3 正确 10 0.000 s 41681 KB 0
4 正确 10 0.001 s 41681 KB 0
5 正确 10 0.001 s 41681 KB 0
6 正确 10 0.001 s 41681 KB 0
7 正确 10 0.054 s 41681 KB 0
8 正确 10 0.125 s 41681 KB 0
9 正确 10 0.192 s 41681 KB 0
10 正确 10 0.190 s 41681 KB 0
运行完成
运行时间 0.564 s
平均内存使用 41681 KB
测试点通过状况 AAAAAAAAAA
得分:100
恭喜你通过了全部测试点!
已完成数据库校验。

2011年11月27日 星期日

NOIP2011_Day1 mayan:用程序解決mayan puzzle遊戲

NOIP2011一試的第三題給我介紹了一個好玩的智力遊戲——Mayan Puzzle。聯賽過後,我從網上下載了這個一夜間變得巨火的遊戲。

在遊戲中,第9關被用來當聯賽那題的說明,第14關就是輸入輸出樣例。

遊戲下載:英文原版、漢化版

首先先誇讚一下遊戲的開發人員,動畫效果做的不錯啊!

我在第8關就碰到了難題,一直過不了關,於是就用聯賽時寫的程序試了一下,結果很棒,程序總能找到正確的解決方案!於是從第8關到第45關都是我用程序解出來的~~

先貼上程序吧:
C++语言: Codee#24246
//NOIP2011-Senior-mayan
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct aaa
{
    int t[6];
    int c[6][8];
    aaa()
    {
        memset(t,0,sizeof(t));
        memset(c,0,sizeof(c));
    }
};

int n,ans[6][3];

bool flag,b[6][8];

aaa solve(aaa g)
{
    aaa h;
    int i,j;
    for(i=1;i<=5;i++)
    {
        for(j=1;j<=g.t[i];j++)
        {
            if(g.c[i][j]&&!b[i][j])
            {
                h.t[i]++;
                h.c[i][h.t[i]]=g.c[i][j];
            }
        }
    }
    return h;
}

aaa clear(aaa g)
{
    int i,j;
    aaa h;
    flag=false;
    memset(b,0,sizeof(b));
    for(i=1;i<=5;i++)
    {
        for(j=1;j<=g.t[i];j++)
        {
            if(i<=3)
            {
                if(g.c[i][j]==g.c[i+1][j]&&g.c[i][j]==g.c[i+2][j])
                {
                    b[i][j]=b[i+1][j]=b[i+2][j]=true;
                    flag=true;
                }
            }
            if(j<=g.t[i]-2)
            {
                if(g.c[i][j]==g.c[i][j+1]&&g.c[i][j]==g.c[i][j+2])
                {
                    b[i][j]=b[i][j+1]=b[i][j+2]=true;
                    flag=true;
                }
            }
        }
    }
    if(flag)
    {
        h=solve(g);
        return clear(h);
    }
    else
    {
        return g;
    }
}

void output()
{
    freopen("mayan.out","w",stdout);
    int i;
    for(i=1;i<=n;i++)
    {
        printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]);
    }
    exit(0);
}

void dfs(aaa g,int k)
{
    int i,j,l;
    aaa h;
    flag=true;
    for(i=1;i<=5;i++)
    {
        if(g.t[i])
        {
            flag=false;
            break;
        }
    }
    if(flag)
    {
        if(k>n)
        {
            output();
        }
        else
        {
            return;
        }
    }
    if(k>n)
    {
        return;
    }
    for(i=1;i<=5;i++)
    {
        for(j=1;j<=g.t[i];j++)
        {
            if(i<5)
            {
                if(j>g.t[i+1])
                {
                    h=g;
                    h.t[i+1]++;
                    h.c[i+1][h.t[i+1]]=h.c[i][j];
                    h.c[i][j]=0;
                    h.t[i]--;
                    for(l=j;l<=h.t[i];l++)
                    {
                        h.c[i][l]=h.c[i][l+1];
                    }
                    h=clear(h);
                    ans[k][0]=i-1;
                    ans[k][1]=j-1;
                    ans[k][2]=1;
                    dfs(h,k+1);
                }
                else
                {
                    h=g;
                    l=h.c[i][j];
                    h.c[i][j]=h.c[i+1][j];
                    h.c[i+1][j]=l;
                    h=clear(h);
                    ans[k][0]=i-1;
                    ans[k][1]=j-1;
                    ans[k][2]=1;
                    dfs(h,k+1);
                }
            }
            if(i>1)
            {
                if(j>g.t[i-1])
                {
                    h=g;
                    h.t[i-1]++;
                    h.c[i-1][h.t[i-1]]=h.c[i][j];
                    h.c[i][j]=0;
                    h.t[i]--;
                    for(l=j;l<=h.t[i];l++)
                    {
                        h.c[i][l]=h.c[i][l+1];
                    }
                    h=clear(h);
                    ans[k][0]=i-1;
                    ans[k][1]=j-1;
                    ans[k][2]=-1;
                    dfs(h,k+1);
                }
            }
        }
    }
}

int main()
{
    freopen("mayan.in","r",stdin);
    freopen("mayan.out","w",stdout);
    int i,j;
    aaa g;
    scanf("%d",&n);
    for(i=1;i<=5;i++)
    {
        scanf("%d",&j);
        while(j)
        {
            g.t[i]++;
            g.c[i][g.t[i]]=j;
            scanf("%d",&j);
        }
    }
    dfs(g,1);
    printf("-1\n");
    return 0;
}

比如第10關,把它轉換為程序的輸入文件就是這樣:
mayan.in
3
1 2 2 0
2 0
1 0
3 1 3 0
1 3 1 0

運行程序,輸出文件是:
mayan.out
0 0 1
3 2 1
3 0 1

於是我按照程序的“指示”移動方塊,沒想到果然過關了!

2011年11月26日 星期六

NOIP2011普及組 複賽 解題報告

pdf試題下載
1.數字反轉 reverse
【問題描述】
給定一個整數,請將該數各個位上數字反轉得到一個新數。新數也應滿足整數的常見形式,即除非給定的原數爲零,否則反轉後得到的新數的最高位數字不應爲零(參見樣例2)。
【輸入】
輸入文件名爲reverse.in。
輸入共1 行,一個整數N。
【輸出】
輸出文件名爲reverse.out。
輸出共1 行,一個整數,表示反轉後的新數。
【輸入輸出樣例1】
reverse.in
123
reverse.out
321
【輸入輸出樣例2】
reverse.in
-380
reverse.out
-83
【數據範圍】
-1,000,000,000 ≤ N≤ 1,000,000,000。

【分析】
普及組的水題,直接模擬即可。
注意:
   1.讀入時用字符串讀入。
   2.注意輸入“0”的情況。

【我的代碼】
C++语言: Codee#24218
//NOIP2011_Junior_reverse
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{
    freopen("reverse.in","r",stdin);
    freopen("reverse.out","w",stdout);
    char line[100]={'\0'};
    scanf("%s\n",&line);
    int s=0;
    int len=strlen(line);
    len--;
    if(line[0]=='0')
        printf("0\n");
    else
    {
        if(line[0]=='-')
        {
            s++;
            printf("-");
        }
        while(line[len--]=='0');
        for (int i=len+1;i>=s;i--)
            printf("%c",line[i]);
        printf("\n");
    }
    return 0;
}


【評測結果】

正在连接评测机...

已连接到评测机
GRID 1
名称 Flitty
系统版本 1.00
备注 COGS 1号评测机 Flitty
正在编译...
编译成功

测试点 结果 得分 运行时间 内存使用 退出代码
1 正确 10 0.000 s 273 KB 0
2 正确 10 0.000 s 273 KB 0
3 正确 10 0.000 s 273 KB 0
4 正确 10 0.000 s 273 KB 0
5 正确 10 0.000 s 273 KB 0
6 正确 10 0.000 s 273 KB 0
7 正确 10 0.000 s 273 KB 0
8 正确 10 0.000 s 273 KB 0
9 正确 10 0.000 s 273 KB 0
10 正确 10 0.000 s 273 KB 0
运行完成
运行时间 0.003 s
平均内存使用 273 KB
测试点通过状况 AAAAAAAAAA
得分:100
恭喜你通过了全部测试点!

2.統計單詞數 stat
【問題描述】
一般的文本編輯器都有查找單詞的功能,該功能可以快速定位特定單詞在文章中的位置,有的還能統計出特定單詞在文章中出現的次數。
現在,請你編程實現這一功能,具體要求是:給定一個單詞,請你輸出它在給定的文章中出現的次數和第一次出現的位置。注意:匹配單詞時,不區分大小寫,但要求完全匹配,即給定單詞必須與文章中的某一獨立單詞在不區分大小寫的情況下完全相同(參見樣例1),如果給定單詞僅是文章中某一單詞的一部分則不算匹配(參見樣例2)。
【輸入】
輸入文件名爲stat.in,2 行。
第1 行爲一個字符串,其中只含字母,表示給定單詞;
第2 行爲一個字符串,其中只可能包含字母和空格,表示給定的文章。
【輸出】
輸出文件名爲stat.out。
只有一行,如果在文章中找到給定單詞則輸出兩個整數,兩個整數之間用一個空格隔開,分別是單詞在文章中出現的次數和第一次出現的位置(即在文章中第一次出現時,單詞首字母在文章中的位置,位置從0 開始);如果單詞在文章中沒有出現,則直接輸出一個整數-1。
【輸入輸出樣例1】
stat.in
To
to be or not to be is a question
stat.out
2 0
【輸入輸出樣例1 說明】
輸出結果表示給定的單詞To 在文章中出現兩次,第一次出現的位置爲0。
【輸入輸出樣例2】
stat.in
to
Did the Ottoman Empire lose its power at that time
stat.out
-1
【輸入輸出樣例2 說明】
表示給定的單詞to 在文章中沒有出現,輸出整數-1。
【數據範圍】
1 ≤ 單詞長度≤ 10。
1 ≤ 文章長度≤ 1,000,000。
 【分析】
這是NOIP2011複賽普及組中的第二道字符串處理題了~
讀入時都把大寫字母轉換成小寫,以方便判斷。
然後就很簡單了,直接暴力枚舉即可。
如果是string類,則之間用“=”號判等;如果是char型數組,用strcmp函數判等。


注意:
1.文章中空格有可能不止1個,要加以判斷。
2.只有strcmp函數返回0時才表示兩個字符數組相等。

【我的代碼】
C++语言: Codee#24217
//NOIP2011_Junior_stat
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int main()
{
    freopen("stat.in","r",stdin);
    freopen("stat.out","w",stdout);
    char str[11];
    memset(str,'\0',sizeof(str));
    int len;
    cin>>str;
    len=strlen(str);
    for (int i=0;i<len;i++)
        str[i]=tolower(str[i]);
  
    string art;
    char tmp[1000001]={'\0'};
    int pos=-1;
    int tot=0;
    int tl;
    getline(cin,art);
    getline(cin,art);
    tl=art.length();
    for (int i=0;i<tl;i++)
        tmp[i]=art[i];
  
    int i=0;
    while(i<tl)
    {
        if(isalpha(tmp[i]))
        {
            int j=i;
            int k=0;
            char arr[1001]={'\0'};
            while(isalpha(tmp[j]))
            {
                arr[k]=tolower(tmp[j]);
                k++;
                j++;
            }
          
            if(strcmp(str,arr)==0)
            {
                tot++;
                if(pos==-1)
                    pos=i;
            }
            i=j+1;
        }
        else
            i++;
    }
  
    if(tot!=0)
        cout<<tot<<" "<<pos<<endl;
    else
        cout<<-1<<endl;
    return 0;
}



【評測結果】

正在连接评测机...

已连接到评测机
GRID 1
名称 Flitty
系统版本 1.00
备注 COGS 1号评测机 Flitty
正在编译...
编译成功

测试点 结果 得分 运行时间 内存使用 退出代码
1 正确 10 0.001 s 1172 KB 0
2 正确 10 0.001 s 1172 KB 0
3 正确 10 0.001 s 1172 KB 0
4 正确 10 0.001 s 1168 KB 0
5 正确 10 0.003 s 1168 KB 0
6 正确 10 0.018 s 1176 KB 0
7 正确 10 0.018 s 1172 KB 0
8 正确 10 0.169 s 1172 KB 0
9 正确 10 0.167 s 1172 KB 0
10 正确 10 0.167 s 1176 KB 0
运行完成
运行时间 0.547 s
平均内存使用 1172 KB
测试点通过状况 AAAAAAAAAA
得分:100
恭喜你通过了全部测试点!


3.瑞士輪 swiss
【背景】
在雙人對決的競技性比賽,如乒乓球、羽毛球、國際象棋中,最常見的賽制是淘汰賽和循環賽。前者的特點是比賽場數少,每場都緊張刺激,但偶然性較高。後者的特點是較爲公平,偶然性較低,但比賽過程往往十分冗長。
本題中介紹的瑞士輪賽制,因最早使用於1895 年在瑞士舉辦的國際象棋比賽而得名。
它可以看作是淘汰賽與循環賽的折衷,既保證了比賽的穩定性,又能使賽程不至於過長。

【問題描述】
2*N 名編號爲1~2N 的選手共進行R 輪比賽。每輪比賽開始前,以及所有比賽結束後,都會按照總分從高到低對選手進行一次排名。選手的總分爲第一輪開始前的初始分數加上已參加過的所有比賽的得分和。總分相同的,約定編號較小的選手排名靠前。
每輪比賽的對陣安排與該輪比賽開始前的排名有關:第1 名和第2 名、第3 名和第4名、……、第2K – 1 名和第2K 名、…… 、第2N – 1 名和第2N 名,各進行一場比賽。每場比賽勝者得1 分,負者得0 分。也就是說除了首輪以外,其它輪比賽的安排均不能事先確定,而是要取決於選手在之前比賽中的表現。
現給定每個選手的初始分數及其實力值,試計算在R 輪比賽過後,排名第Q 的選手編號是多少。我們假設選手的實力值兩兩不同,且每場比賽中實力值較高的總能獲勝。
【輸入】
輸入文件名爲swiss.in。
輸入的第一行是三個正整數N、R、Q,每兩個數之間用一個空格隔開,表示有2*N 名選手、R 輪比賽,以及我們關心的名次Q。
第二行是2*N 個非負整數s1, s2, …, s2N,每兩個數之間用一個空格隔開,其中si 表示編號爲i 的選手的初始分數。
第三行是2*N 個正整數w1, w2, …, w2N,每兩個數之間用一個空格隔開,其中wi 表示編號爲i 的選手的實力值。
【輸出】
輸出文件名爲swiss.out。
輸出只有一行,包含一個整數,即R 輪比賽結束後,排名第Q 的選手的編號。
【輸入輸出樣例】
swiss.in
2 4 2
7 6 6 7
10 5 20 15
swiss.out
1
【輸入輸出樣例說明】


本輪對陣 本輪結束後的得分
選手編號 / ① ② ③ ④
初始 / 7 6 6 7
第1 輪 ①—④ ②—③ 7 6 7 8
第2 輪 ④—① ③—② 7 6 8 9
第3 輪 ④—③ ①—② 8 6 9 9
第4 輪 ③—④ ①—② 9 6 10 9
【數據範圍】
對於30%的數據,1 ≤ N≤ 100;
對於50%的數據,1 ≤ N≤ 10,000;
對於100%的數據,1 ≤ N≤ 100,000,1 ≤ R≤ 50,1 ≤ Q≤ 2N,0 ≤ s1, s2, …, s2N ≤ 10^8,1 ≤ w1,
w2, …, w2N ≤ 10^8。

【分析】
這是普及組的一道難題。
首先,由於每輪比賽的次序都是由上一輪比賽完後的比分決定的,因此對於普及組的同學來說僅能使用模擬的方法來解決。容易想到的是每一輪模擬完以後快排一次,用這樣的方法,時間複雜度爲O(2Nlog2N*R),根據數據範圍可知這種思路僅能解決50%的數據。因此我們需要一種更有效的方式。
分析題意後可以發現,對於每一輪比賽過後,每一位選手都會有一種狀態,贏了或者輸了,而每一位選手比賽前都是有序的,那麼對於所有在該輪比賽中贏了或者是輸了的選手,都是有序的。即每輪比賽後,我們可以以O(N)的效率獲得勝負選手的有序隊列,對於兩組有序序列,容易想到將兩組數列歸併,歸併的效率爲O(N),這樣,完成一輪比賽的效率從O(2Nlog2N*R)縮減至O(NR),根據數據範圍,這樣的程序是能夠在1秒內出解的。

【我的代碼】
C++语言: Codee#24224
/*
*Problem:NOIP2011_Junior_Swiss 瑞士輪
*Author:Yee-fan Zhu
*School:Henan Experimental High School
*Method:Merge,QuickSort
*Date:2011.11.26
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
class SWISS
{
public:
    int num;
    int val;
    int tot;
}P[200003];
int N,R,Q;

SWISS A[100001],B[100001];
int pa,pb;

int cmp(const void *a,const void *b)
{
    class SWISS *c=(class SWISS *)a;
    class SWISS *d=(class SWISS *)b;
    if(c->tot!=d->tot)
        return d->tot-c->tot;
    return c->num-d->num;
}

void init()
{
    scanf("%d %d %d\n",&N,&R,&Q);
    N*=2;
    for(int i=1;i<=N;i++)
    {   
        P[i].num=i;
        scanf("%d",&P[i].tot);
    }
   
    for(int i=1;i<=N;i++)
        scanf("%d",&P[i].val);
    qsort(P+1,N,sizeof(SWISS),cmp);
}

void merge()
{
    int p=0;
    int i=1,j=1;
    while(i<=N/2&&j<=N/2)
    {
        if(A[i].tot>B[j].tot)
        {
            P[++p]=A[i++];
            continue;
        }
        if(A[i].tot==B[j].tot)
        {
            if(A[i].num<B[j].num)
            {   
                P[++p]=A[i++];
                continue;
            }
            P[++p]=B[j++];
            continue;
        }
        P[++p]=B[j++];
    }
    while(i<=N/2) P[++p]=A[i++];
    while(j<=N/2) P[++p]=B[j++];
}

void work()
{
    for (int i=1;i<=R;i++)
    {
        int j=1;
        pa=0,pb=0;
        while(j<N)
        {
            if(P[j].val>P[j+1].val)
                P[j].tot++,A[++pa]=P[j],B[++pb]=P[j+1];
            else
                P[j+1].tot++,A[++pa]=P[j+1],B[++pb]=P[j];
            j+=2;
        }
        merge();
    }
   
    qsort(P+1,N,sizeof(SWISS),cmp);
    printf("%d\n",P[Q].num);
}

int main()
{
    freopen("swiss.in","r",stdin);
    freopen("swiss.out","w",stdout);
    init();
    work();
    return 0;
}
【評測結果】
正在连接评测机...

已连接到评测机
GRID 1
名称 Flitty
系统版本 1.00
备注 COGS 1号评测机 Flitty
正在编译...
编译成功

测试点 结果 得分 运行时间 内存使用 退出代码
1 正确 10 0.000 s 4960 KB 0
2 正确 10 0.000 s 4960 KB 0
3 正确 10 0.001 s 4960 KB 0
4 正确 10 0.019 s 4960 KB 0
5 正确 10 0.047 s 4960 KB 0
6 正确 10 0.123 s 4960 KB 0
7 正确 10 0.254 s 4960 KB 0
8 正确 10 0.468 s 4960 KB 0
9 正确 10 0.543 s 4960 KB 0
10 正确 10 0.612 s 4960 KB 0
运行完成
运行时间 2.068 s
平均内存使用 4960 KB
测试点通过状况 AAAAAAAAAA
得分:100
恭喜你通过了全部测试点!