【例7.4】 循環比賽日程表


1325:【例7.4】 循環比賽日程表


時間限制: 1000 ms         內存限制: 65536 KB
提交數: 1717     通過數: 901 

【題目描述】

設有NN個選手進行循環比賽,其中N=2^M,要求每名選手要與其他N1N−1名選手都賽一次,每名選手每天比賽一次,循環賽共進行N1N−1天,要求每天沒有選手輪空。

【輸入】

輸入:M

【輸出】

輸出:表格形式的比賽安排表。一行各數據間用一個空格隔開。

【輸入樣例】

3

【輸出樣例】

1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1

例題不怎么詳的解:
解題時的我:
這題乍一看挺簡單的,拿着題目想了5分鍾就可以得出大致思路,我一開始想到了其實就想到了找規律,但是雖然找到了規律,卻不敢下手去碼,因為是新的一章。於是我先把這個思路放了放,轉向另一種思路。
想到了純模擬,用各種數組記錄安排啊什么的,發現這種辦法實在太麻煩,而且極其容易超時,根本不是辦法。
最后看了下書上的算法分析,恍然大悟,原來真nm是找規律,於是乎我又回到了剛開始的思路。

從題目所給的例子,眼尖的童鞋一眼就會發現左斜着的一排全是1,右斜着的一排全是8。如果有接觸過鄰接鏈表,你就會驚訝地發現這也是一個具有對稱性的表格啊!?
繼續觀察,果不其然,發現對角對稱性,即左上的部分和右下的部分完全一致,右上和左下的部分完全一致。完全一致啊!!!

深入這道題目本身的設定,原來是因為比賽的場次都會兩兩相對地重復。1和2打的要記錄,2和1打的也要記錄。
那么接下來就好辦事了,這樣一把題目分析,就找到了運用分治算法解題的思路,就是從左上角開始,用一個遞推將整個表計算出來。

算法分析:
首先是設置遞推邊界,即將模擬數組左上角的第一個數為1,然后遞推構造左上角,將左上角乘以2再復制到右上角,最后分別構造左下和右下。
得到遞推式:
以a為模擬數組,設置一個half變量來記錄所需復制的表格塊的大小(此處為關鍵點);
構造右上角:a[i][j+half]=a[i][j]+half;
構造左下角:a[i+half][j]=a[i][j+half];
構造右下角:a[i+half][j+half]=a[i][j];

由於是復制左上和右上角的數據,所以構造右上角要寫在前面。

顯而易見,總共需要構造的輪數恰好是2的指數級別,也就是說每多構造一次表格的長和寬都會乘以2。
因此,我們的主體循環結構只用循環M次。
為了再每個循環中構造新的部分,我們必須設置一個關鍵變量half來控制每次增加的表格塊,每次循環乘以2,對應表格的大小。
得到如下核心代碼:
  a[0][0]=1;
    while(k<=m)
    {
        for(int i=0;i<half;i++)
            for(int j=0;j<half;j++)
                a[i][j+half]=a[i][j]+half;//構造右上角
 
        for(int i=0;i<half;i++)
            for(int j=0;j<half;j++)
            {
                a[i+half][j]=a[i][j+half];//構造左下角
                a[i+half][j+half]=a[i][j];//構造右下角
            }
        half*=2;
        k++;
    }

 有一個騷操作我必須要講,因為某個二進制位每向左移一位,相應的十進制數就會增大2倍!這個操作可以極大地優化代碼的時間復雜度,省去了用循環求2的n次方步驟。

樣例代碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<string>
#define INF 999999999
#define N 1001
#define MOD 1000000007
using namespace std;
int a[N][N];
int main()
{
    int m;
    int k=1,half=1;
    cin>>m;
 
    int n=1<<m;
    a[0][0]=1;
    while(k<=m)
    {
        for(int i=0;i<half;i++)
            for(int j=0;j<half;j++)
                a[i][j+half]=a[i][j]+half;
 
        for(int i=0;i<half;i++)
            for(int j=0;j<half;j++)
            {
                a[i+half][j]=a[i][j+half];
                a[i+half][j+half]=a[i][j];
            }
        half*=2;
        k++;
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
            printf("%d ",a[i][j]);
        cout<<endl;
    }
    return 0;
}

2019-02-12 13:41:52

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM