【CJ】ForestUniversity

一看可以误差0.03就知道是一个强行模拟出一个概率的方法。

首先想到的是,根据课程之间的拓扑关系,可以每次精确生成一个合法的排课顺序。(设集合S是当前可以选的所有课程的集合,最初S置为所有没有前置课程的课的集合。然后每选一门课,就有若干们课加入集合S。)

问题是,怎么让每种合法的排课顺序等概率被随机到。

注意到没有课程最多只有一个前置,这样就保证拓扑关系是一颗树。

考虑最基础的一种情况,

一棵树,和一个点的情况。

设树上一共有N个节点,那么对于这个树的一种合法排课,在N+1个位置把点插入,那么会生成N+1个合法的排课。

所以必须要让随机到树根的概率是点的N倍。

同样的,树和点处于对称位置,所以猜测,每个点取到的概率必须要和它的子树大小成正比。

然后就是一些细节了,比如生成随机数的时候,最好加上一个大数,或者自己写一个靠谱的伪随机函数。

int index = (rand() + (rand() << 15)) % S.size();

没有这个操作的我WA的很久。

最后贴一发代码:

#include<iostream>
#include<fstream>
#include<vector>
#include<algorithm>
#include<string>
#include<set>
#include<cstdlib>
#include <time.h>

using namespace std;

int isSubstr(string p,string s) {
    int c = 0;
    int Ls = s.length();
    int Lp = p.length();
    if (!Lp) return 0;
    vector<int> pi = vector<int>(Lp + 1, 0);
    for (int i = 2; i <= Lp; i++)
    {
        while (c && p[i - 1] != p[c]) c = pi[c];
        if (p[i - 1] == p[c]) c++;
        pi[i] = c;
    }
    c = 0;
    for (int i = 0; i<Ls; i++)
    {
        while (c && s[i] != p[c]) c = pi[c];
        if (s[i] == p[c]) c++;
        if (c == Lp) return true;
    }
    return false;
}

int count(int node, vector<int>& childs,vector<vector<int>>& uncover)
{
    int ret = 1;
    for (auto x : uncover[node])
    ret += count(x,childs,uncover);
    childs[node] = ret;
    return ret;
}

int main()
{

    srand(time(NULL));

    ifstream fin("in.txt");
    ofstream fout("out.txt");

    int T;
    fin >> T;
    for (int t = 0; t < T; t++)
    {
        fout << "Case #" << t + 1 << ": ";
        
        int N;
        fin >> N;
        auto pre = vector<int>(N,0);
        auto uncover = vector<vector<int>>(N, vector<int>{});
        for (int i = 0; i < N; i++)
        {
            fin >> pre[i];
            if (pre[i])
            uncover[pre[i]-1].push_back(i);
        }
        string CourseName;
        fin >> CourseName;
        int M;
        fin >> M;
        auto CoolName = vector<string>(M,"");
        for (int i = 0; i < M; i++) fin >> CoolName[i];
        
        int Times = 30000;
        auto ans = vector<int>(M,0);
        
        //
        auto childs = vector<int>(N,0);
        for (int i = 0; i < N;i++)
        if (!childs[i]) count(i,childs,uncover);

        //for (auto x : childs) cout << x << " ";
        //cout << endl;

        for (int times = 0; times < Times;times++)
        {
            //cout << "pre:";
            //for (auto x : pre) cout << x << " ";
            //cout << endl;
            
            vector<int> S;
            for (int i = 0; i < N; i++) 
            if (!pre[i])
            for (int j = 0; j < childs[i]; j++)
            S.push_back(i);
            
            
            //fout << "S0:";
            //for (auto x : S) fout << x << " ";
            //fout << endl;

            string tmp = "";
            for (int i = 0; i < N; i++)
            {
                int index = (rand() + (rand() << 15)) % S.size();
                int selectedCourse = S[index];
                int li = index; 
                int ri = index;
                while (li - 1 >= 0 && S[li - 1] == S[index]) li--;
                while (ri + 1 <= S.size()-1 && S[ri + 1] == S[index]) ri++;

                /*/
                fout << endl;
                fout << "--b---" << endl;
                fout << "size " << S.size() << endl;
                fout << "index " << index << endl;
                fout << "list: " << endl;
                for (auto x : S) fout << x << " ";
                fout << endl;
                fout << "li" << li << endl;
                fout << "ri" << ri << endl;
                fout << "--e---" << endl;
                /*/

                S.erase(S.begin() + li,S.begin() + ri + 1);
                for (auto x : uncover[selectedCourse]) 
                for (int j = 0; j < childs[x];j++)
                S.push_back(x);
                tmp = tmp + CourseName[selectedCourse];
            }
            //fout << tmp << endl;
            for (int i = 0; i < M;i++)
            if (isSubstr(CoolName[i], tmp)) ans[i]++;
        }
        
        fout.precision(2);
        for (int i = 0; i < M; i++)
            fout << fixed <<static_cast<double>(ans[i]) / Times  << (i == M - 1 ? '\n' : ' ');

    }

    return 0;
}

 

posted @ 2016-06-17 14:37  syb3181  阅读(229)  评论(0编辑  收藏  举报