【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; }