学习博客

bzoj4720: [Noip2016]换教室

//https://www.luogu.org/blog/user22136/solution-p1850
#include<bits/stdc++.h>
using namespace std;
double w1,w2,w3,w4,dis[302][302],f[2002][2],c[2002],z,ans;
int n,m,v,e,i,j,k,a[2002],b[2002],x,y;
int main(){
    scanf("%d%d%d%d",&n,&m,&v,&e);
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    for (i=1;i<=n;i++) scanf("%d",&b[i]);
    for (i=1;i<=n;i++) scanf("%lf",&c[i]);
    for (i=1;i<=v;i++)
        for (j=1;j<i;j++) dis[i][j]=dis[j][i]=1e9;
    for (i=1;i<=e;i++) scanf("%d%d%lf",&x,&y,&z),dis[x][y]=dis[y][x]=min(dis[x][y],z);
    for (k=1;k<=v;k++)
        for (i=1;i<=v;i++)
            for (j=1;j<i;j++) dis[i][j]=dis[j][i]=min(dis[i][j],dis[i][k]+dis[k][j]);
    for (i=0;i<=m;i++) f[i][0]=f[i][1]=1e9;
    f[0][0]=f[1][1]=0;
    for (i=2;i<=n;i++){
        w1=dis[a[i-1]][a[i]];
        w2=dis[b[i-1]][a[i]]*c[i-1]+dis[a[i-1]][a[i]]*(1-c[i-1]);
        w3=dis[a[i-1]][b[i]]*c[i]+dis[a[i-1]][a[i]]*(1-c[i]);
        w4=dis[b[i-1]][b[i]]*c[i-1]*c[i]+dis[b[i-1]][a[i]]*c[i-1]*(1-c[i])+
        dis[a[i-1]][a[i]]*(1-c[i-1])*(1-c[i])+dis[a[i-1]][b[i]]*(1-c[i-1])*c[i];
        for (j=min(i,m);j>=0;j--){
            f[j][0]=min(f[j][0]+w1,f[j][1]+w2);
            if (j) f[j][1]=min(f[j-1][0]+w3,f[j-1][1]+w4);
        }
    }
    ans=1e9;
    for (i=0;i<=m;i++) ans=min(ans,min(f[i][0],f[i][1]));
    printf("%.2lf",ans);
}

HDU 3853 LOOPS

/*https://www.cnblogs.com/kuangbin/archive/2012/10/03/2711140.html dp[i][j]表示(i,j)到(R,C)需要消耗的能量 则:dp[i][j]=p1[i][j]*dp[i][j]+p2[i][j]*dp[i][j+1]+p3[i][j]*dp[i+1][j]+2; 化简得到: dp[i][j]=p2[i][j]*dp[i][j+1]/(1-p1[i][j])+p3[i][j]*dp[i+1][j]/(1-p1[i][j])+2/(1-p1[i][j]); 注意一种情况就是p1[i][j]==1的情况。 题目只是保证答案小于1000000.但是有的点可能永远都不可能到达的。 所以这样的点出现p1[i][j]是允许的。 否则就会WA了。*/
#include<bits/stdc++.h>
using namespace std;
const int N=1003;
int r,c,i,j;
double p1[N][N],p2[N][N],p3[N][N],f[N][N];
int main(){
    while (~scanf("%d%d",&r,&c)){
        for (i=1;i<=r;i++)
            for (j=1;j<=c;j++) scanf("%lf%lf%lf",&p1[i][j],&p2[i][j],&p3[i][j]);
        memset(f,0,sizeof(f));
        for (i=r;i;i--)
            for (j=c;j;j--)
                if ((i!=r || j!=c) && 1-p1[i][j]>1e-6)
                f[i][j]=(f[i][j+1]*p2[i][j]+f[i+1][j]*p3[i][j]+2)/(1-p1[i][j]);
        printf("%.3lf\n",f[1][1]);
    }
}

hdu 4035 Maze

/*https://www.cnblogs.com/kuangbin/archive/2012/10/03/2711108.html dp求期望的题。 题意: 有n个房间,由n-1条隧道连通起来,实际上就形成了一棵树, 从结点1出发,开始走,在每个结点i都有3种可能: 1.被杀死,回到结点1处(概率为ki) 2.找到出口,走出迷宫 (概率为ei) 3.和该点相连有m条边,随机走一条 求:走出迷宫所要走的边数的期望值。 设 E[i]表示在结点i处,要走出迷宫所要走的边数的期望。E[1]即为所求。 叶子结点: E[i] = ki*E[1] + ei*0 + (1-ki-ei)*(E[father[i]] + 1); = ki*E[1] + (1-ki-ei)*E[father[i]] + (1-ki-ei); 非叶子结点:(m为与结点相连的边数) E[i] = ki*E[1] + ei*0 + (1-ki-ei)/m*( E[father[i]]+1 + ∑( E[child[i]]+1 ) ); = ki*E[1] + (1-ki-ei)/m*E[father[i]] + (1-ki-ei)/m*∑(E[child[i]]) + (1-ki-ei); 设对每个结点:E[i] = Ai*E[1] + Bi*E[father[i]] + Ci; 对于非叶子结点i,设j为i的孩子结点,则 ∑(E[child[i]]) = ∑E[j] = ∑(Aj*E[1] + Bj*E[father[j]] + Cj) = ∑(Aj*E[1] + Bj*E[i] + Cj) 带入上面的式子得 (1 - (1-ki-ei)/m*∑Bj)*E[i] = (ki+(1-ki-ei)/m*∑Aj)*E[1] + (1-ki-ei)/m*E[father[i]] + (1-ki-ei) + (1-ki-ei)/m*∑Cj; 由此可得 Ai = (ki+(1-ki-ei)/m*∑Aj) / (1 - (1-ki-ei)/m*∑Bj); Bi = (1-ki-ei)/m / (1 - (1-ki-ei)/m*∑Bj); Ci = ( (1-ki-ei)+(1-ki-ei)/m*∑Cj ) / (1 - (1-ki-ei)/m*∑Bj); 对于叶子结点 Ai = ki; Bi = 1 - ki - ei; Ci = 1 - ki - ei; 从叶子结点开始,直到算出 A1,B1,C1; E[1] = A1*E[1] + B1*0 + C1; 所以E[1] = C1 / (1 - A1); 若 A1趋近于1则无解... */
#include<bits/stdc++.h>
using namespace std;
const int N=10003;
const double eps=1e-9;
struct node{
    int to,ne;
}E[N<<1];
int T,i,x,y,h[N],tot,n,Case;
double A[N],B[N],C[N],k[N],e[N];
void add(int x,int y){
    E[++tot]=(node){y,h[x]};
    h[x]=tot;
}
bool dfs(int u,int pre){
    int m=0;
    double ta=0,tb=0,tc=0,t=1-k[u]-e[u];
    for (int i=h[u],v;i;i=E[i].ne,m++)
        if ((v=E[i].to)!=pre){
            if (!dfs(v,u)) return 0;
            ta+=A[v];tb+=B[v];tc+=C[v];
        }
    ta*=t/m;tb*=t/m;tc*=t/m;
    A[u]=k[u]+ta;
    B[u]=t/m;
    C[u]=t+tc;
    if (fabs(1-tb)<eps) return 0;
    A[u]/=1-tb;
    B[u]/=1-tb;
    C[u]/=1-tb;
    return 1;
}
int main(){
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        tot=0;
        memset(h,0,(n+1)<<2);
        for (i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
        for (i=1;i<=n;i++) scanf("%lf%lf",&k[i],&e[i]),k[i]/=100,e[i]/=100;;
        printf("Case %d: ",++Case);
        if (dfs(1,0) && fabs(1-A[1])>eps) printf("%.6lf\n",C[1]/(1-A[1]));
        else printf("impossible\n");
    }
}

HDU 4336Card Collector

/*https://www.cnblogs.com/kuangbin/archive/2012/10/06/2713089.html*/
#include<bits/stdc++.h>
using namespace std;
int n,i,j;
double p[20],f[1<<20],sum,s;
int main(){
    while (~scanf("%d",&n)){
        for (i=0;i<n;i++) scanf("%lf",&p[i]);
        f[(1<<n)-1]=0;
        for (i=(1<<n)-2;i>=0;i--){
            s=0;sum=1;
            for (j=0;j<n;j++)
                if (!(i&(1<<j))) sum+=p[j]*f[i|(1<<j)],s+=p[j];
            f[i]=sum/s;
        }
        printf("%.5lf\n",f[0]);
    }
}

zoj3640Help Me Escape

//https://blog.csdn.net/sprithy_dream/article/details/8044200
#include<bits/stdc++.h>
using namespace std;
double dp[200003];
int c[103],t[103],n,f,i;
double dfs(int x){
    if (dp[x]>0) return dp[x];
    for (int i=0;i<n;i++){
        if (x>c[i]) dp[x]+=t[i];
        else dp[x]+=1+dfs(x+c[i]);
    }
    return dp[x]/=n;
}
int main(){
    while (~scanf("%d%d",&n,&f)){
        for (i=0;i<n;i++) scanf("%d",&c[i]),t[i]=(1+sqrt(5))/2*c[i]*c[i];
        memset(dp,0,sizeof(dp));
        printf("%.3lf\n",dfs(f));
    }
}

zoj3380 Patchouli’s Spell Cards

# https://blog.csdn.net/s_h_r/article/details/49025111
import sys

C = [([0] * 110) for i in range(110)]
for i in range(101):
    C[i][0] = C[i][i] = 1
    for j in range(1, i):
        C[i][j] = C[i-1][j] + C[i-1][j-1]

def GCD(a, b):
    return a if not b else GCD(b, a % b)

while True:
    try:
        m, n, l = map(int, sys.stdin.readline().split())
        if(l > m):
            print('mukyu~')
            continue
        dp = [([0] * 110) for i in range(110)]
        dp[0][0] = 1
        for i in range(1, n+1):
            for j in range(1, m+1):
                for k in range(0, min(j+1, l)):
                    dp[i][j] += dp[i-1][j-k] * C[m-(j-k)][k]
        Sum = pow(n, m)
        ans = 0
        for i in range(1, n+1):
            ans += dp[i][m]
        ans = Sum - ans;
        gcd = GCD(ans, Sum)
        print str(ans / gcd) + '/' + str(Sum / gcd)
    except:
        break

Poj3071 Football

/*https://blog.csdn.net/xuechelingxiao/article/details/38520105 dp[i][j]表示第 i 轮的时候,第 j 支队伍赢的概率 dp[i][j] = ∑(dp[i-1][j]*dp[i-1][k]*p[j][k]); 对于其中位运算,可以用一个二叉树来更直观的理解 (j>>(i-1))^1) 跟 (k>>(i-1)分别表示一个父节点的两个子节点, 当(j>>(i-1))^1) == (k>>(i-1)时,表明两个子节点是竞争关系,胜利者将更新到父节点。 */
#include<cstdio>
#include<cstring>
using namespace std;
int n,i,j,k;
double f[8][1<<7],p[1<<7][1<<7];
int main(){
    while (~scanf("%d",&n)){
        if (n==-1) break;
        for (i=0;i<1<<n;i++)
            for (j=0;j<1<<n;j++) scanf("%lf",&p[i][j]);
        memset(f,0,sizeof(f));
        for (i=0;i<1<<n;i++) f[0][i]=1;
        for (i=1;i<=n;i++)
            for (j=0;j<1<<n;j++)
                for (k=0;k<1<<n;k++)
                    if (((j>>(i-1))^(k>>(i-1)))==1) f[i][j]+=f[i-1][j]*f[i-1][k]*p[j][k];
        k=0;
        for (i=1;i<1<<n;i++)
            if (f[n][i]>f[n][k]) k=i;
        printf("%d\n",k+1);
    }
}

sgu495 Kids and Prizes

/*https://blog.csdn.net/L__emon/article/details/44655971 题意:有n个奖品,奖品各自放在盒子里,m个人轮流选取,若盒中有奖品则拿走,但不管怎样,盒子依然放回。问得到奖品数的期望。 思路:1)数学推理 每个奖品不被选中的概率为(1-1/n)^m,那么每个奖品被选中的概率为1-(1-1/n)^m; 所以总期望为n*(1-(1-1/n)^m); 2)概率dp dp[i]表示到第i个人得到奖品数的期望;所以dp[i]等于上一个人得到的奖品数加上这个人得到的奖品数; 若此人得到了奖品则概率为(n-dp[i-1])/n, 若没得到则概率为dp[i-1]/n; 所以dp[i] = dp[i-1]+1*(n-dp[i-1])/n+0*dp[i-1]/n; */
#include<bits/stdc++.h>
using namespace std;
int n,m,i;
double x,ans;
int main(){
    scanf("%d%d",&n,&m);
    x=1;
    for (i=1;i<=m;i++){
        ans+=x;
        x=(n-ans)/n;
    }
    printf("%.10lf",ans);
}

POJ 2151Check the difficulty of problems

/*https://blog.csdn.net/u013200703/article/details/47705129 dp[i][j][k] 表示第i个人前面j道题做对了k道的期望 dp[i][j][k] = dp[i][j-1][k]*(1-p[i][j]) + dp[i][j-1][k-1]*p[i][j] */
#include<cstdio>
#include<cstring>
using namespace std;
int k,i,j,n,m,t;
double ans,cnt,sub,dp[35][35],p[35];
int main(){
    while (scanf("%d%d%d",&m,&t,&n)){
        if (!m && !t && !n) break;
        sub=ans=1;
        for (i=1;i<=t;i++){
            memset(dp,0,sizeof(dp));
            dp[0][0]=1;
            for (j=1;j<=m;j++) scanf("%lf",&p[j]);
            for (j=1;j<=m;j++)
                for (k=0;k<=j;k++){
                    dp[j][k]+=dp[j-1][k]*(1-p[j]);
                    if (k>=1) dp[j][k]+=dp[j-1][k-1]*p[j];
                }
            ans*=(1-dp[m][0]);
            cnt=0;
            for (j=1;j<n;j++) cnt+=dp[m][j];
            sub*=cnt;
        }
        printf("%.3f\n",ans-sub);//这里如果是%.3lf就错了,不知道为什么 
    }
}

HDU 4089 Activation

/*https://www.cnblogs.com/kuangbin/archive/2012/10/03/2710987.html 题意:有n个人排队等着在官网上激活游戏。Tomato排在第m个。 对于队列中的第一个人。有一下情况: 1、激活失败,留在队列中等待下一次激活(概率为p1) 2、失去连接,出队列,然后排在队列的最后(概率为p2) 3、激活成功,离开队列(概率为p3) 4、服务器瘫痪,服务器停止激活,所有人都无法激活了。 求服务器瘫痪时Tomato在队列中的位置<=k的概率 解析: 概率DP; 设dp[i][j]表示i个人排队,Tomato排在第j个位置,达到目标状态的概率(j<=i) dp[n][m]就是所求 j==1: dp[i][1]=p1*dp[i][1]+p2*dp[i][i]+p4; 2<=j<=k: dp[i][j]=p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1]+p4; k<j<=i: dp[i][j]=p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1]; 化简: j==1: dp[i][1]=p*dp[i][i]+p41; 2<=j<=k: dp[i][j]=p*dp[i][j-1]+p31*dp[i-1][j-1]+p41; k<j<=i: dp[i][j]=p*dp[i][j-1]+p31*dp[i-1][j-1]; 其中: p=p2/(1-p1); p31=p3/(1-p1) p41=p4/(1-p1) 可以循环i=1->n 递推求解dp[i].在求解dp[i]的时候dp[i-1]就相当于常数了。 在求解dp[i][1~i]时等到下列i个方程 j==1: dp[i][1]=p*dp[i][i]+c[1]; 2<=j<=k:dp[i][j]=p*dp[i][j-1]+c[j]; k<j=i: dp[i][j]=p*dp[i][j]+c[j]; 其中c[j]都是常数了。上述方程可以解出dp[i]了。 首先是迭代得到 dp[i][i].然后再代入就可以得到所有的dp[i]了。 注意特判一种情况。就是p4<eps时候,就不会崩溃了,应该直接输出0 应要用滚动数组优化,否则内存要炸 */
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-6;
double dp[2][2003],p1,p2,p3,p4,p,p31,p41,c[2003],tmp,pp[2003];
int n,m,i,j,k,x,xx;
int main(){
    while (~scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&k,&p1,&p2,&p3,&p4)){
        if (p4<eps){
            printf("0.00000\n");
            continue;
        }
        p=p2/(1-p1);
        p31=p3/(1-p1);
        p41=p4/(1-p1);
        pp[0]=1;
        for (i=1;i<=n;i++) pp[i]=p*pp[i-1];
        dp[1][1]=p41/(1-p);
        c[1]=p41;
        x=0;xx=1;
        for (i=2;i<=n;i++,x^=1,xx^=1){
            for (j=2;j<=k;j++) c[j]=dp[xx][j-1]*p31+p41;
            for (j=k+1;j<=i;j++) c[j]=dp[xx][j-1]*p31;
            tmp=0;
            for (j=1;j<=i;j++) tmp+=c[j]*pp[i-j];
            dp[x][i]=tmp/(1-pp[i]);
            dp[x][1]=p*dp[x][i]+c[1];
            for (j=2;j<i;j++) dp[x][j]=p*dp[x][j-1]+c[j];
        }
        printf("%.5lf\n",dp[xx][m]);
    }
}

HDU 4405 Aeroplane chess

#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,pos[100003],u,v;
double dis[100003];
int main(){
    while (~scanf("%d%d",&n,&m)){
        if (!n && !m) break;
        for (i=0;i<=n;i++) pos[i]=0;
        for (i=0;i<m;i++) scanf("%d%d",&u,&v),pos[u]=v;
        for (i=n;i<=n+6;i++) dis[i]=0;
        for (i=n-1;i>=0;i--)
            if (!pos[i]){
                dis[i]=1;
                for (j=1;j<=6;j++) dis[i]+=dis[i+j]/6;
            }else dis[i]=dis[pos[i]];
        printf("%.4lf\n",dis[0]);
    }
}

ZOJ 3329 One Person Game

/*http://www.cnblogs.com/kuangbin/archive/2012/10/03/2710648.html 题意:有三个骰子,分别有k1,k2,k3个面。 每次掷骰子,如果三个面分别为a,b,c则分数置0,否则加上三个骰子的分数之和。 当分数大于n时结束。求游戏的期望步数。初始分数为0 设dp[i]表示达到i分时到达目标状态的期望,pk为投掷k分的概率,p0为回到0的概率 则dp[i]=∑(pk*dp[i+k])+dp[0]*p0+1; 都和dp[0]有关系,而且dp[0]就是我们所求,为常数 设dp[i]=A[i]*dp[0]+B[i]; 代入上述方程右边得到: dp[i]=∑(pk*A[i+k]*dp[0]+pk*B[i+k])+dp[0]*p0+1 =(∑(pk*A[i+k])+p0)dp[0]+∑(pk*B[i+k])+1; 明显A[i]=(∑(pk*A[i+k])+p0) B[i]=∑(pk*B[i+k])+1 先递推求得A[0]和B[0]. 那么 dp[0]=B[0]/(1-A[0]); */
#include<bits/stdc++.h>
using namespace std;
int T,n,k1,k2,k3,k,i,j,a,b,c;
double A[603],B[603],p[103],p0;
int main(){
    scanf("%d",&T);
    while (T--){
        scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
        p0=1.0/k1/k2/k3;
        memset(p,0,sizeof(p));
        for (i=1;i<=k1;i++)
            for (j=1;j<=k2;j++)
                for (k=1;k<=k3;k++)
                    if (i!=a || j!=b || k!=c) p[i+j+k]+=p0;
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        for (i=n;i>=0;i--){
            A[i]=p0;B[i]=1;
            for (j=1;j<=k1+k2+k3;j++) A[i]+=A[i+j]*p[j],B[i]+=B[i+j]*p[j];
        }
        printf("%.16lf\n",B[0]/(1-A[0]));
    }
}

POJ 2096 Collecting Bugs

/*https://blog.csdn.net/roney_win/article/details/9822753 题意:一个软件有s个子系统,会产生n种bug。 某人一天发现一个bug,这个bug属于某种bug,发生在某个子系统中。 求找到所有的n种bug,且每个子系统都找到bug,这样所要的天数的期望。 需要注意的是:bug的数量是无穷大的,所以发现一个bug,出现在某个子系统的概率是1/s, 属于某种类型的概率是1/n。 解法: dp[i][j]表示已经找到i种bug,并存在于j个子系统中,要达到目标状态的天数的期望。 显然,dp[n][s]=0,因为已经达到目标了。而dp[0][0]就是我们要求的答案。 dp[i][j]状态可以转化成以下四种: dp[i][j] 发现一个bug属于已经找到的i种bug和j个子系统中 dp[i+1][j] 发现一个bug属于新的一种bug,但属于已经找到的j种子系统 dp[i][j+1] 发现一个bug属于已经找到的i种bug,但属于新的子系统 dp[i+1][j+1]发现一个bug属于新的一种bug和新的一个子系统 以上四种的概率分别为: p1 = i*j / (n*s) p2 = (n-i)*j / (n*s) p3 = i*(s-j) / (n*s) p4 = (n-i)*(s-j) / (n*s) 又有:期望可以分解成多个子期望的加权和,权为子期望发生的概率,即 E(aA+bB+...) = aE(A) + bE(B) +... 所以: dp[i,j] = p1*dp[i,j] + p2*dp[i+1,j] + p3*dp[i,j+1] + p4*dp[i+1,j+1] + 1; 整理得: dp[i,j] = ( 1 + p2*dp[i+1,j] + p3*dp[i,j+1] + p4*dp[i+1,j+1] )/( 1-p1 ) = ( n*s + (n-i)*j*dp[i+1,j] + i*(s-j)*dp[i,j+1] + (n-i)*(s-j)*dp[i+1,j+1] )/( n*s - i*j ) */
#include<cstdio>
#include<cstring>
using namespace std;
int i,j;
double ns,f[1003][1003],n,s;
int main(){
    scanf("%lf%lf",&n,&s);
    ns=n*s;
    for (i=n;i>=0;i--)
        for (j=s;j>=0;j--)
            if (i!=n || j!=s) f[i][j]=(f[i+1][j]*(n-i)*j+f[i][j+1]*i*(s-j)+f[i+1][j+1]*(n-i)*(s-j)+ns)/(ns-i*j);
    printf("%.4f\n",f[0][0]);
}

cf148D Bag of mice

/*http://www.cnblogs.com/kuangbin/archive/2012/10/04/2711184.html 题意: 原来袋子里有w只白鼠和b只黑鼠 龙和王妃轮流从袋子里抓老鼠。谁先抓到白色老师谁就赢。 王妃每次抓一只老鼠,龙每次抓完一只老鼠之后会有一只老鼠跑出来。 每次抓老鼠和跑出来的老鼠都是随机的。 如果两个人都没有抓到白色老鼠则龙赢。王妃先抓。 问王妃赢的概率。 解析: 设dp[i][j]表示现在轮到王妃抓时有i只白鼠,j只黑鼠,王妃赢的概率 明显 dp[0][j]=0,0<=j<=b;因为没有白色老鼠了 dp[i][0]=1,1<=i<=w;因为都是白色老鼠,抓一次肯定赢了。 dp[i][j]可以转化成下列四种状态: 1、王妃抓到一只白鼠,则王妃赢了,概率为i/(i+j); 2、王妃抓到一只黑鼠,龙抓到一只白鼠,则王妃输了,概率为j/(i+j)*i/(i+j-1). 3、王妃抓到一只黑鼠,龙抓到一只黑鼠,跑出来一只黑鼠,则转移到dp[i][j-3]。 概率为j/(i+j)*(j-1)/(i+j-1)*(j-2)/(i+j-2); 4、王妃抓到一只黑鼠,龙抓到一只黑鼠,跑出来一只白鼠,则转移到dp[i-1][j-2]. 概率为j/(i+j)*(j-1)/(i+j-1)*i/(i+j-2); 当然后面两种情况要保证合法,即第三种情况要至少3只黑鼠,第四种情况要至少2只白鼠 */
#include<bits/stdc++.h>
using namespace std;
int i,j,w,b;
double f[1003][1003];
int main(){
    while (~scanf("%d%d",&w,&b)){
        memset(f,0,sizeof(f));
        for (i=1;i<=w;i++) f[i][0]=1;
        for (i=1;i<=w;i++)
            for (j=1;j<=b;j++){
                f[i][j]=1.0*i/(i+j);
                if (j>=3) f[i][j]+=1.0*j/(i+j)*(j-1)/(i+j-1)*(j-2)/(i+j-2)*f[i][j-3];
                if (j>=2) f[i][j]+=1.0*j/(i+j)*(j-1)/(i+j-1)*i/(i+j-2)*f[i-1][j-2];
            }
        printf("%.10lf\n",f[w][b]);
    }
}