POJ 3071 FOOTBALL
题意:n<=7 2^n个人,知道p[i][j]:i打败j的概率.1和2比,3-4比..胜利者进入下一轮继续,问获胜概率最大是谁?
设dp[i][j]第i个人赢j轮的概率 dp[i][j]+=dp[i][j-1]*dp[k][j-1]*p[i][k] k范围?
利用位运算来推导:2^n个人对应一棵二叉树,从下数第j层代表轮数,若第i个人在第j层的左子树中 则j层的右子树结点都可以在第j轮作为i的对手
找到规律 i>>(j-1)^1==k>>(j-1) i,k表示第j层中的左右子树的中某个结点
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
const int N=2e3+20;
const int inf=2e8;
double dp[N][N];//dp[i][j]第i个人赢j轮的概率
double p[N][N];
int main()
{
int n;
while(cin>>n&&n!=-1)
{
int m=1<<n;
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
scanf("%lf",&p[i][j]);
int s,e;
memset(dp,0,sizeof(dp));
for(int i=0;i<m;i++)
dp[i][0]=1.0;
for(int j=1;j<=n;j++)
{
for(int i=0;i<m;i++)
{
for(int k=0;k<m;k++)
{
//相邻
if(((i>>(j-1))^1)==(k>>(j-1)))
dp[i][j]+=dp[i][j-1]*dp[k][j-1]*p[i][k];
}
}
}
double res=0;
int ans;
for(int i=0;i<m;i++)
{
if(dp[i][n]>res)
res=dp[i][n],ans=i+1;
}
cout<<ans<<endl;
}
return 0;
}
POJ 2151 Check the difficulty of Problems
题意:T只队伍,m个问题,T<=1000,m<=30,p[i][j] 第i人解决问题j的概率,问冠军至少答对n题并且所有人都至少答对一题的概率?P1:所有人都至少对一题 等于第i个人至少对一题累乘,求每个i时利用对立事件(1-对0题的概率)即可
P2:所有人对的题目1<=x<=n-1的概率
P2为P1的子集 P1-P2为所有人都至少对一题 && 至少存在一个人(冠军)答题数>=n
dp[k][i][j]第k个人前i题对j题的概率
m*t<=3e4 暴力枚举第i人 答对的题为1~n-1 即可计算出P2
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
const int N=1e3+20;
const int inf=2e8;
double dp[N][35][35];//dp[k][i][j]第k个人前i题对j题的概率
double p[N][N];
int m,t,n;
int main()
{
while(cin>>m>>t>>n&&(m+t+n))
{
for(int i=1;i<=t;i++)
for(int j=1;j<=m;j++)
scanf("%lf",&p[i][j]);
memset(dp,0,sizeof(dp));
for(int i=1;i<=t;i++)
dp[i][0][0]=1;
for(int k=1;k<=t;k++)
{
for(int i=1;i<=m;i++)
{
for(int j=0;j<=i;j++)
dp[k][i][j]=dp[k][i-1][j]*(1.0-p[k][i])+(j-1<0?0:dp[k][i-1][j-1]*p[k][i]);
}
}
double ans=1;
for(int k=1;k<=t;k++)
ans*=(1.0-dp[k][m][0]);//第k个人至少对一题
double res=1;
for(int k=1;k<=t;k++)
{
double cnt=0;
for(int i=1;i<n;i++)
{
cnt+=dp[k][m][i];
}
res*=cnt;
}
if(n==1)
res=0;
ans-=res;
printf("%.3lf\n",ans);
}
return 0;
}
Codeforces 540D Bad Luck Island
题意:a kill b b kill c c kill a 给出abc个数 问最后只剩下a的概率 a,b,c<=100
设dp[a][b][c] 表示三种种类为abc时 a获胜概率 按照每次选两个不同的种类来转移状态即可 .
也可以设dp[a][b][c]为还剩下abc时的概率,最后累加dp[i][j][0]即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> ii;
const int N=1e2+20;
const ll inf=2e15;
double f[N][N][N];
int a,b,c;
double DP(int a,int b,int c)
{
if(a==0||(b==0&&c))
return f[a][b][c]=0;
if(f[a][b][c]!=-1)
return f[a][b][c];
if(c==0)
return f[a][b][c]=1;
double res=0,s=a+b+c,sum=a*(a-1)/2 + b*(b-1)/2+c*(c-1)/2;
s=s*(s-1)/2.0;
s-=sum;
if(b&&c)
res+=1.0*b*c/s*DP(a,b,c-1);
if(a&&b)
res+=1.0*a*b/s*DP(a,b-1,c);
if(a&&c)
res+=1.0*a*c/s*DP(a-1,b,c);
return f[a][b][c]=res;
}
void init()
{
for(int i=0;i<=100;i++)
for(int j=0;j<=100;j++)
for(int k=0;k<=100;k++)
f[i][j][k]=-1;
}
int main()
{
while(cin>>a>>b>>c)
{
double x,y,z;
init();
DP(a,b,c);
x=f[a][b][c];
init();
DP(b,c,a);
y=f[b][c][a];
z=1-x-y;
printf("%.12lf %.12lf %.12lf\n",x,y,z);
}
return 0;
}
Codeforces 148D Bag of Mice
题意:w个白球,b个黑球,P,D两人轮流抽球,先抽到白球的人胜利,D抽球时,剩下的球会随机消失掉一个(剧情需要..),若游戏结束没人抽到白球则D获胜.
w,b<=1000 问P获胜的概率?
设DP[w][b]为球个数为wb时 P获胜的概率 s=w+b
w/s直接获胜,否则P抽到黑球后再转移到D抽到黑球后的状态(P要转移到下一个状态决策时,说明游戏还没结束,即P还没输)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e3+5;
int a,b;
double d[N][N];
void init()
{
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
d[i][j]=-1;
}
double DP(int a,int b)
{
// printf("%d %d\n",a,b);
if(a==0)
return d[a][b]=0;
if(d[a][b]!=-1)
return d[a][b];
double res=0,s=a+b,num=1.0*b/s*(b-1)/(s-1);
res+=1.0*a/s;
if(b>=2)
res+=1.0*num*a/(s-2)*DP(a-1,b-2);
if(b>=3)
res+=1.0*num*(b-2)/(s-2)*(DP(a,b-3));
return d[a][b]=res;
}
int main()
{
while(cin>>a>>b)
{
init();
printf("%.9lf\n",DP(a,b));
}
return 0;
}