 /**//*
要從n<=13個人中通過黑白配多去少留(相等的話繼續(xù)),如果只有兩個人則剪刀石頭布來選出一個苦力。
要求選出苦力所用時間的期望,還有哪些人最有可能成為苦力,及其成為苦力的概率。
給出每個人出黑、白、石頭、剪刀、布的概率u,d,r,p,s
瞄到watashi博客上的枚舉子集
我就嘗試,dp[mask]表示當前這些人離目標的期望次數(shù)
然后轉(zhuǎn)移就是枚舉多少人出黑、白,如果兩個人時就枚舉石頭、剪刀、布
dp[mask] = 1+∑up[sub]*down[other]*dp[bitcount[sub] < bitcount[other] ? sub : other] , other = mask ^ sub即other是sub關(guān)于mask的補集
不過這里有一些是bitcount[sub] = bitcount[other] 或者 bitcount[sub] = 0,bitcount[other] = 0的情況,此時轉(zhuǎn)移到mask
將這些項移到左邊去即可,我看watashi是不轉(zhuǎn)移兩種情況
而每個人成為苦力的概率時,是指"從初始狀態(tài)到只剩一個人時(或者Infinity)的這個事件發(fā)生的概率"
就可以通過從最初dp[(1<<n)-1]不斷乘以一些決策的概率轉(zhuǎn)移到下一種事件
因為有可能一次決策后還是沒有人去掉,這樣mask還是變回到了mask
這里,我看watashi算概率時,只算那些有轉(zhuǎn)移的事件。
比如狀態(tài)1->1概率為p1,1->2為p2,1->3為p3 ,則認為從p1轉(zhuǎn)移到p2的概率為 p2/(p2+p3),即分母是可以轉(zhuǎn)移的狀態(tài)的概率之和
我的理解是,即使轉(zhuǎn)移到1,到最后結(jié)束時,肯定是轉(zhuǎn)移到2或3!!!!
這樣1轉(zhuǎn)移到2的概率就為: p2+p1*p2+p1*p1*p2+ = p2*(1+p1+p1^2 ) = p2*(1-p1^n)/(1-p1) = p2/(1-p1)
其實也就是轉(zhuǎn)移到1的概率最后還是會按比例分配到2,3,所以不用考慮轉(zhuǎn)移到自身的
原來這個就是“歸一化”

我之前以為每個人lose的概率是指這些人lose的ratio,這樣他們之和就為1了
watashi:"不是的,你看樣例就有一個是0.0% + 0.0% < 1的。加上死循環(huán)的概率之和才是1"

復雜度就是枚舉子集再枚舉子集,2^k*C(n,k) = (2+1)^n = 3^n
這題要先預處理一些東西,轉(zhuǎn)移時直接用,不然會超時吧
可能看到n=13,n=12時就應(yīng)該要猜到是O(3^n)的做法了,2010福州warm up D題就是O(3^12)

好了,到這里怎么算是比較明確的
但這題比較多的細節(jié),比如inf,nan(not a number),要避免算出nan
如果說為了避免除0,當遇到分母時就賦值為自定義的INF時,注意這個INF繼續(xù)參與運算后,結(jié)果不一定變?yōu)镮NF了
如(0.1*1e30+0.9*1)+1 << 1e30 !!!!!
所以即使除0,沒事的,直接除,系統(tǒng)會給予以個inf的值

而inf的值參與運算時幾個注意的地方:
inf*1e-300 還是inf
+inf + +inf = +inf 但+inf + -inf = nan
inf*0 = nan
0/0 = nan
inf/inf = nan
inf*inf = inf 符號跟平常乘法的一樣
感覺跟數(shù)學的無窮大、無窮小的運算類似,0/0 , +∞ - +∞, ∞/∞ 等是未定義

g++的math.h就有isinf(),isfinite(),isnan()函數(shù)用了
*/
#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<cmath>
#include<string>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<set>
#include<list>
#include<numeric>
#include<cassert>
#include<ctime>
#include<bitset>

using namespace std;

const double EPS = 1e-8;

double dp[1<<13], u[13], d[13], r[13], p[13], s[13];
double up[1<<13], down[1<<13];
double lose[13][13];
int bitcount[1<<13];
string name[13];

int main()
  {
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
#endif
 for (int mask = 0; mask < (1<<13); mask ++) {
bitcount[mask] = bitcount[mask>>1] + (mask&1);
}
int T,n;
 for (scanf("%d", &T); T--; ) {
scanf("%d", &n);
 for (int i = 0; i < n; i ++) {
cin>>name[i];

cin>>u[i]>>d[i];
double sum = u[i] + d[i];
u[i] /= sum;
d[i] /= sum;

cin>>r[i]>>p[i]>>s[i];
sum = r[i]+p[i]+s[i];
r[i] /= sum;
p[i] /= sum;
s[i] /= sum;
}
up[0] = down[0] = 1.0;
 for(int mask = 1; mask < (1<<n); mask ++) {
int x;
 for(int i = 0; i < n ; i ++) {
 if(mask & (1<<i)) {
x = i;
break;
}
}
up[mask] = up[mask^(1<<x)]*u[x];
down[mask] = down[mask^(1<<x)]*d[x];
}
//x lose to y
 for(int x = 0; x < n; x++) {
 for(int y = 0; y < n ; y++) {
lose[x][y] = r[x]*p[y] + p[x]*s[y] + s[x]*r[y];
}
}

 for ( int mask = 1; mask < (1<<n) ; mask ++) {
 if(bitcount[mask] == 1) {
dp[mask] = 0.0;
 }else if(bitcount[mask] == 2) {
int x = -1, y;
 for(int i = 0; i < n ; i ++) {
 if(mask & (1<<i)) {
x == -1 ? x = i : y = i;
}
}
dp[mask] = 1.0 / (lose[x][y]+lose[y][x]);
 }else {
double div = 0, mul = 1.0;
 for(int sub = mask & (mask - 1) ; sub > 0 ; sub = mask & (sub - 1)) {
int other = mask ^ sub;
 if(bitcount[sub] != bitcount[other] && up[sub] > 0 && down[other] > 0) {//要判斷>0,否則為0時跟下面的dp[<?sub:other]相乘就會為NaN!!!
div += up[sub]*down[other];
mul += up[sub]*down[other]*dp[bitcount[sub] < bitcount[other] ? sub : other];
}
}
dp[mask] = mul / div;
}
}
 if(dp[(1<<n) - 1] > 1e30) {
printf("Infinity ");
 }else {
printf("%.3f ", dp[(1<<n) - 1] );
}

double ans = 0;
fill(dp, dp+(1<<n), 0);
dp[(1<<n) -1] = 1.0;
 for (int mask = (1<<n) - 1; mask > 0 ; mask -- ) {
 if(dp[mask] == 0) {
continue;
}
 if(bitcount[mask] == 1) {
ans = max(ans, dp[mask]);
 }else if(bitcount[mask] == 2) {
int x = -1, y;
 for(int i = 0; i < n ; i ++) {
 if(mask & (1<<i)) {
x == -1 ? x = i : y = i;
}
}
 if(lose[x][y] + lose[y][x] > 0) {//0/0會算出NaN,而為0時,答案也就為0,不用算
dp[1<<x] += lose[x][y] / (lose[x][y] + lose[y][x]) * dp[mask];
dp[1<<y] += lose[y][x] / (lose[x][y] + lose[y][x]) * dp[mask];
}
 }else {
double div = 0;
 for(int sub = mask & (mask - 1) ; sub > 0 ; sub = mask & (sub - 1) ) {
int other = mask ^ sub;
 if(bitcount[sub] != bitcount[other] && up[sub] > 0 && down[other] > 0) {
div += up[sub]*down[other];
}
}
 if(div == 0) {
continue;
}
 for(int sub = mask & (mask - 1) ; sub > 0 ; sub = mask & (sub - 1) ) {
int other = mask ^ sub;
 if(bitcount[sub] != bitcount[other] && up[sub] > 0 && down[other] > 0) {
dp[bitcount[sub] < bitcount[other] ? sub : other] += up[sub]*down[other]/div*dp[mask];
}
}
}
}

vector<string> vt;
 for(int i = 0 ; i < n ; i ++ ) {
 if(fabs(dp[1<<i] - ans ) < EPS) {
vt.push_back(name[i]);
}
}
sort(vt.begin(), vt.end());
printf("%.3f%%\n", ans*100);
 for(vector<string>::iterator it = vt.begin() ; it != vt.end() ; it++) {
 if(it != vt.begin()) {
putchar(' ');
}
cout<<*it;
}
cout<<endl;
}
return 0;
}
|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|