SRM495 Div1 Medium(500) CarrotBoxes
強連結成分分解して、他から情報が得られない成分のみ開ければ良い。そのような成分の個数をxとすると成功する確率は(N-x)/N。ただし、最後に回すことで箱が1つだけ残るような成分があれば、その箱を開けずとも空と分かるので、確率は(N-x+1)/N。
#include <vector> #include <string> #include <algorithm> using namespace std; // 強連結成分分解 // 辺を受け取り、各ノードが属する成分を返す vector<int>scc(vector<vector<bool> >E) { int n=(int)E.size(); vector<int>v1(n,-1),v2(n); // 帰りがけ順で番号付け for(int i=n,c=0;i--;)if(v1[i]<0){ vector<int>p(1,i); while(!p.empty()){ int t=p.back(); v1[t]=0; for(int j=n;j--;)if(j!=t&&E[t][j]&&v1[j]<0)p.push_back(j); if(p.back()==t)p.pop_back(),v1[t]=c++;}} // 番号が最大のノードから到達可能な点を同じ成分に for(int c=0,m;m=max_element(&v1[0],&v1[0]+n)-&v1[0],v1[m]>=0;c++){ vector<int>p(1,m); while(!p.empty()){ int t=p.back(); v1[t]=-1,v2[t]=c; for(int j=n;j--;)if(j!=t&&E[j][t]&&v1[j]>=0)p.push_back(j); if(p.back()==t)p.pop_back();}} return v2; } void dfs( vector<vector<bool> > E, vector<bool> *V, int p ) { (*V)[p] = true; for ( int i=0; i<(int)E.size(); i++ ) if ( E[p][i] && ! (*V)[i] ) dfs(E,V,i); } class CarrotBoxes{public: double theProbability( vector <string> information ) { int n = (int)information.size(); vector<vector<bool> > E(n,vector<bool>(n)); for ( int i=0; i<n; i++ ) for ( int j=0; j<n; j++ ) E[i][j] = information[i][j] == 'Y'; vector<int> v = scc(E); // 強連結成分のうち他の成分からの辺が無い成分を候補とする vector<int> cand; for ( int i=0; i<=*max_element(v.begin(),v.end()); i++ ) { bool f = true; for ( int j=0; j<n; j++ ) for ( int k=0; k<n; k++ ) if ( v[j]!=i && v[k]==i && E[j][k] ) f = false; if ( f ) cand.push_back(i); } // 最後にしたときにその候補1つだけが残るような候補があるならば、 // その候補を最後にすることで確率が上がる for ( int i=0; i<(int)cand.size(); i++ ) { vector<bool> t(n,false); for ( int j=0; j<(int)cand.size(); j++ ) if ( j != i ) { int k; for ( k=0; k<n; k++ ) if ( v[k] == cand[j] ) break; dfs(E,&t,k); } if ( count(t.begin(),t.end(),false) == 1 ) return (double)(n-cand.size()+1)/n; } return (double)(n-cand.size())/n; }};