1회 시행에서 1행0열로 3으로 나눈 값이 복제 (이긴 경우)
0행1열로 3으로 나눈 값이 복제 ( 진 경우)
0행0열에 3으로 나눈 값이 남음 (비긴 경우)
2회 시행에서 [1,0]/3 -> [2,0]로, [0,1]/3 -> [0,2]로,
([1,0]+[0,1])/3 -> [1,1]로 각각 복제
원래 각 셀이 가지고 있던 값의 1/3이 남고, 1/3이 아래로 더해지고, 1/3이 우로 더해지면서 게임이 진행
n행에 도달하여 사각테이블을 벗어나는 값은 게임에 이긴 경우로 승리확률(w)에 누적되고 n열에 도달하여 사각테이블을 벗어나는 값은 게임에 진 경우
게임에 이긴 경우와 진 경우는 0행0열에 더해져 게임 진행
그리고, 게임을 계속할지 포기할지를 판단하기 위해 미리 다음과 같은 확률을 준비했습니다.
u[0]: 게임을 포기하고 재시작하는 확률.
정상적으로 게임을 진행한다는 가정으로 확률 계산
0행0열에서 1값을 가지고 게임 시작
u[k]: 게임을 포기없이 계속하는 확률
k (= r * n + c) r행c열에서 게임을 계속하는 확률
r행c열에서 1값을 가지고 게임 시작
본 게임은 각각의 시행 후에 진 경우가 많을 때 즉 r < c인 셀에 한하여 u[0]와 u[k]를 비교하여 u[0]가 크면 포기하는 경우로 해당 셀을 재시작하기 위해 b에 누적하고 0로 만듭니다
이와같이 게임을 진행하면 초기에 변동이 심하고 중간에 증가량이 오차범위 내에서 일정하게 증가하다가 말기에 다시 변동성이 증가합니다.
그래서 일정하게 증가하는 중간부분을 건너뛰기 위해 변칙처리 했습니다.
1000회를 넘는 경우 749회까지 시행후 750회의 증가량으로 중간부분을 계산하였고 나머지 250회의 시행으로 말기의 변동성이 큰부분을 커버했습니다.
#include <stdio.h>#include <memory.h>#include <vector>usingnamespacestd;classRule{vector<double>p;public:Rule(intr,intc);~Rule(){p.clear();}doublewin(intt);};// w:승리확률 저장, b:재게임 저장ints,n;Rule*u[100];doublew,b,a[100];inlinedoublemove()// 게임을 사각테이블(nxn)로 모형화{// 이전 종료게임을 재시작시키고 초기화intk,x=n*n-1;a[0]+=b;b=0.0;// n회 먼저 승리한 게임. 재시작을 위해 b에 저장// n회 먼저 패배한 게임. 재시작을 위해 b에 저장for(k=n;k--;)b+=a[x-k]/3.0;w+=b;for(k=x;k>0;k-=n)b+=a[k]/3.0;// 1회 시행후 이기면 아래로, 지면 우로 이동, 비기면 제자리while(x>=n){k=n-1;while(k--){a[x]=(a[x]+a[x-n]+a[x-1])/3.0;x--;}a[x]=(a[x]+a[x-n])/3.0;x--;}while(x){a[x]=(a[x]+a[x-1])/3.0;x--;}a[0]/=3.0;returnw;}Rule::Rule(intr,intc){// 사각테이블 초기화 및 시작위치 설정memset(a,0,8*n*n);a[r*n+c]=1.0;w=b=0.0;// 1000회의 시행이면 증가폭이 오차범위 내에서 움직인다고 봄.// 이후 일정하게 증가함.inte=(s<1000)?s:1000;p.assign(e,0.0);// 최대 1000회 시행 승리확률을 벡터 p에 저장.for(intt=1;t<e;t++)p[t]=move();}doubleRule::win(intt){// 계속시행or재시행시 잔여시행의 승리확률 가져옴(비교용).intx=s-t;returnx<1000?p[x]:p[999];}inlinevoideval(int&t,inte){intr,c,k;while(t<=e){move();// 패배가 많은 경우 포기or계속 결정. u[0]:재시작, u[k]:계속시행for(r=0;r<n-1;r++)for(c=n-1;c>r;c--){if(u[0]->win(t)>u[k=r*n+c]->win(t)){b+=a[k];a[k]=0.0;}}t++;}}doubleplay(){// u[0] : 재시작시 승리확률 미리 계산// u[k] : 계속시행 승리확률 미리 계산intt,r,c;u[0]=newRule(0,0);for(r=0;r<n-1;r++)for(c=n-1;c>r;c--)u[r*n+c]=newRule(r,c);memset(a,0,8*n*n);a[0]=1.0;w=b=0.0;t=1;// s가 충분히 크면 초기에 변동성이 크다가 중간에서 일정하게 증가후// 말기에 다시 변동성 증가함. 일정하게 증가하는 중간부분 건너뜀if(s>1000){eval(t,749);doubled=w;eval(t,750);d=w-d;w+=(s-1000)*d;t=s-249;}eval(t,s);for(r=0;r<n-1;r++)for(c=n-1;c>r;c--)deleteu[r*n+c];deleteu[0];returnw;}intmain(){intt;scanf("%d",&t);while(t--){scanf("%d %d",&s,&n);printf("%.8f\n",(n>1)?play():((double)s/3.0));}return0;}
음.. 어떤 풀이인지 제가 잘 이해가 되지 않아 도움을 드리기 어렵습니다. 일단 대강은 맞는 것 같은데 답의 정밀도가 문제가 되는 것 같네요. 큰 데이터에 대해 올려 주신 코드를 돌린 결과와 상대 오차를 계산하면 대략 0.1% 수준 정도네요. 상당히 큰 정밀도 향상이 필요할 것 같습니다.
web3p
잘 안되네요. 뭔가 잘못 생각하고 있는거 같은데 고수님들의 도움 부탁드립니다.
먼저, 게임을 사각테이블(nxn)로 모형화했습니다.
그리고, 게임을 계속할지 포기할지를 판단하기 위해 미리 다음과 같은 확률을 준비했습니다.
본 게임은 각각의 시행 후에 진 경우가 많을 때 즉 r < c인 셀에 한하여 u[0]와 u[k]를 비교하여 u[0]가 크면 포기하는 경우로 해당 셀을 재시작하기 위해 b에 누적하고 0로 만듭니다
이와같이 게임을 진행하면 초기에 변동이 심하고 중간에 증가량이 오차범위 내에서 일정하게 증가하다가 말기에 다시 변동성이 증가합니다.
그래서 일정하게 증가하는 중간부분을 건너뛰기 위해 변칙처리 했습니다.
1000회를 넘는 경우 749회까지 시행후 750회의 증가량으로 중간부분을 계산하였고 나머지 250회의 시행으로 말기의 변동성이 큰부분을 커버했습니다.
11년 전