[特征根法 || 高斯消元] 51Nod 1653 算法马拉松19 E 夹克赌坊

这道题先是很happy的打了个高斯消元


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long double ld;

const int N=200005;
const ld eps=1e-15;

int S,W,A,n;
ld p1,p2;

ld a[N][5],ans;

int main(){
  int T; double _p1,_p2;
  scanf("%d",&T);
  while (T--){
    scanf("%d%d%d%lf%lf",&S,&W,&A,&_p1,&_p2); p1=_p1+eps; p2=_p2+eps;
    n=S+W;
    for (int i=0;i<=n;i++){
    	if (i-1>=1) 
    		a[i][1]=i-1<=S-A?-p2:-p1;
    	a[i][2]=1;
    	if (i+1<n)
    		a[i][3]=i+1<=S-A?(p2-1.0):(p1-1.0);
    }
    a[S][0]=1;
    for (int i=0;i<n;i++){
      a[i+1][2]-=a[i][3]*a[i+1][1]/a[i][2];
      a[i+1][0]-=a[i][0]*a[i+1][1]/a[i][2];
      a[i+1][1]=0;
    }
    ans=a[n][0]/a[n][2];
    printf("%.15lf\n",(double)(ans+eps));
    cl(a);
  }
  return 0;
}


然后WA了 打出了个乱七八糟的东西 通过Python卡精度卡了一天半 成功卡过


from decimal import Decimal
from decimal import getcontext
getcontext().prec =65

Zero=Decimal('0.000000000')
One=Decimal('1.000000000')

def AA(i,k):
    if k==1:
        if i-1>=1:
            return ((i-1<=S-A) and p2) or p1
        else:
            return Zero
    else:
        if i+1<n:
            return ((i+1<=S-A) and _p2) or _p1
        else:
            return Zero
            
T=int(input())
for t in range(T):
    nums=input().split();
    S=int(nums[0])
    W=int(nums[1])
    A=int(nums[2])
    p1=Decimal(nums[3])
    p2=Decimal(nums[4])
    _p1=One-p1
    _p2=One-p2
    n=S+W
    xx=One
    Ans=One
    for i in range(n):
        xx=One-AA(i,3)*AA(i+1,1)/xx
        if i+1>=S:
            Ans=Ans/xx
        if i+1>S:
            Ans=Ans*AA(i+1,1)
    print(Ans)

卡过后我仰天长啸 比赛结束后看题解发现自己好傻逼 这么明显的特征根

不过我总觉得题解有那么一点点问题 按照自己想法改过之后的题解应该是这样的


设p(i)为你当前收益为i的时刻条件下能最终获胜的概率
不难列出转移方程,
-S<i<=-A时,p(i)=Pa*p(i+1)+(1-Pa)*p(i-1)  ....  (1)
-A<i<W时,p(i)=Pb*p(i+1)+(1-Pb)*p(i-1)      ....  (2)
转移方程(1)、(2)都不难通过特征根的方法求解通项公式;

对于(1)、(2)你会分别获得两个同构的通项公式,不妨假设为f(i)与g(i),f(i)为(1)式子获得,g(i)为(2)式子获得。即
-S<i<=-A时,p(i)=f(i)
-A<i<W时,p(i)=g(i)

f、g中一共有4个未知数,我们可以通过一些边界条件求解,边界包括:
f(-S) = 0  ..... (3)
g(W)  = 1  ..... (4)
f(-A) = g(-A)  ... (5)
f(-A+1)=g(-A+1) .. (6)
(3)~(6)可以通过高斯消元求解,也可以手工求解并直接写成表达式。

最后注意代码的精度问题。(怕麻烦的话不如用python的decimal)


然后上代码


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
//typedef __float128 ld;
typedef long double ld;

const ld eps=1e-15;

int S,W,A,n;
ld p1,p2,xa,xb,Ans;

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  int T; double _p1,_p2;
  scanf("%d",&T);
  while (T--){
    scanf("%d%d%d%lf%lf",&S,&W,&A,&_p1,&_p2); p1=_p1+eps; p2=_p2+eps;
    xa=(1-p1)/p1; xb=(1-p2)/p2;
    Ans=1.0+(xb-1)*(1-pow(xa,W))/((xb-1)*(pow(xa,W)-pow(xa,-A))+pow(xa,-A)*(xa-1)*(1-pow(xb,-S+A)));
    printf("%.14lf\n",(double)Ans);
  }
  return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值