零、前言

為了保證程式的正確,我們需要生成資料進行檢驗,這就需要使用到資料生成器.
本文就講講怎麼生成幾種OI中的常見資料.

一、排列

1、問題:給定N,在O(n)隨機出1—N的一個排列

2、STL

  • 時間複雜度:O(N)
  • 程式碼:
int t[N];
void makePerm0(void)
{
    for (int i=1;i<=n;i++) t[i]=i;
    random_shuffle(t+1,t+n+1);
}
  • 解釋:呼叫STL的random_shuffle(F,S),可以直接對地址為F到地址為S的位置打亂順序.

3、隨機交換

  • 時間複雜度:O(n)
  • 做法:首先隨機出1—N的一個排列,然後對於第i位,把它和p[rand()%(i-1)+1]進行交換.
  • 程式碼:
int t[N];
void makePerm1(void)
{
    srand(time(0));
    for (int i=1;i<=n;i++) t[i]=i;
    for (int i=2;i<=n;i++) swap(t[i],t[rand()%(i-1)+1]);
}
  • 注意:P黨就只能用第二種了,不過也差不多…

二、樹

1、問題:在O(n)內生成一棵樹

2、方法:往前連邊

  • 對於第i個點,我們把它往前連到rand()%(i-1)+1
  • 程式碼:
void makeTree0(void)
{
    srand(time(0));
    for (int i=2;i<=n;i++)
        printf("%d %d\n",i,rand()%(i-1)+1);
}
  • 證明:樹就是無向無環聯通圖.
    現在要證明它是無環的,且整棵樹聯通.
    ①當n=2時,滿足要求,因為2必定連到1
    ②假設當n<k時都滿足,證明當n=k時亦滿足
    連向的點c滿足1<c<k
    不會產生迴路
    之前全都連在一起
    現在也連在一起
    綜上所述,該演算法可以在O(n)內構造出樹.

3、特殊要求

如果要求1不一定是根,怎麼辦?
我們進行一次makePerm,往回連的時候把t[i]連到t[rand()%(i-1)+1].

三、圖

1、問題:在O(m)內生成一張聯通圖

2、方法:首先建樹,然後剩下的邊隨便

3、特殊要求

  • 要求1:沒有重邊
    ①對於N比較大的情況,每次隨機,每次生成的邊加到set或者Hash裡面
    ②對於N比較小的情況,把所有的邊(u,v)列出來,用random_shuffle()隨機二元組
  • 要求2:沒有自環:遇到自環就重新來
  • 要求3:規定起點,終點連通的有向圖
    ①首先t[1]=s,隨機t[2]到t[n],然後把t[1],t[2],t[3],…,ti進行連邊
    ②對於後面的點,隨機連到前面,方向也進行隨機
    ③最後隨便弄一些邊,完善這一棵樹
int t[N],s,t;
void makeGraph(void)
{
    for (int i=1;i<=n;i++) t[i]=i;
    random_shuffle(t+1,t+n+1);
    for (int i=1;i<=n;i++) if (t[i]==s) {swap(t[i],t[1]);break;}

    int i;
    for (i=1;i<n;i++)
    {
        printf("%d -> %d",t[i],t[i+1]);
        if (t[i+1]==t) break;
    }

    int k;
    for (i++;i<n;i++)
    {
        k=rand()&1;
        if (!k)
            printf("%d -> %d\n",t[i],t[rand()%(i-1)+1]);
        else printf("%d -> %d\n",t[rand()%(i-1)+1],t[i]);
    }
}

4、字串

1、問題:在O(L)之內生成長度為L的字串
2、方法

char s[L]; int len;
int makeString(void)
{
    for (int i=1;i<=len;i++) s[i]=rand()%M+N;
    //M為個數,N為起始字元
}

5、小結

一些常見的資料生成方法上面已經涉及,今天就總結這麼多啦!