【动态规划】单人纸牌

问题

单人纸牌游戏,共36张牌分成9叠,每叠4张牌面向上。每次,游戏者可以从某两个不同的牌堆最顶上取出两张牌面相同的牌(如黑桃10和梅花10)并且一起拿走。如果最后所有纸牌都被取走,则游戏者就赢了,否则游戏者就输了。

George很热衷于玩这个游戏,但是一旦有时有多种选择的方法,George就不知道取哪一种好了,George会从中随机地选择一种走,例如:顶上的9张牌为KS, KH, KD, 9H, 8S, 8D, 7C, 7D,6H,显然有5种取法:(KS, KH), (KS, KD), (KH, KD), (8S, 8D), (7C, 7D),当然George取到每一种取法的概率都是1/5。

有一次,George的朋友Andrew告诉他,这样做是很愚蠢的,不过George不相信,他认为如此玩最后成功的概率是非常大的。请写一个程序帮助George证明他的结论:计算按照他的策略,最后胜利的概率。

输入数据

9行每行4组用空格分开的字串,每个字串两个字符,分别表示牌面和花色,按照从堆底到堆顶的顺序给出。

输出数据

一行,最后胜利的概率,精确到小数点后6位。

样例输入

AS 9S 6C KS

JC QH AC KH

7S QD JD KD

QS TS JS 9H

6D TD AD 8S

QC TH KC 8D

8C 9D TC 7C

9C 7H JH 7D

8H 6S AH 6H

样例输出

0.589314

分析

用到动态规划的方法,由于给定的牌的排列是一定的。我们完全可以开一个九维的数组f[x1,x2,x3......x9]表示每堆取走多少张纸牌,每次从最上端选取相同的两张纸牌(不考虑花色),则有这样的方程f[x1,x2....x9]:=sum{f[x1',x2',x3'...x9']/total}其中x'为取走牌之前的状态,total为能够达到当前状态的状态的总数,因为做除数,所以要对其进行是否为0的分类讨论。这里采用记忆化搜索。枚举从哪两个上面取牌,先进行总数的统计再进行除法。

这里再对方程进行详细的解释,对于某个状态k来说,假设已知有n个状态能达到它,这些状态出现的概率为pi,显然该状态的概率等于每个能达到它的状态的概率和(加法原理)。对于每个状态来说,在当前状态被选择的概率为(1/n),所以每个状态的最终可加概率便为(pi*1/n)(乘法原理),所以最终的状态就是方程所示。

至于边界条件,f[0,0,0,....0]:=1;

View Code
program liukeke;
var
a:
array[0..9,1..4] of char;
f:
array[0..4,0..4,0..4,0..4,0..4,0..4,0..4,0..4,0..4] of double;

procedure init;
var
i,j:longint;
ch,kong:char;
begin
for i:=1 to 9 do
for j:=1 to 4 do
begin
read(a[i,j]);
read(ch);
read(kong);
if j=4 then readln;
end;
end;

function find(x1,x2,x3,x4,x5,x6,x7,x8,x9:longint):double;
var
i,j,sum:longint;
x:
array[1..9] of longint;
begin
x[
1]:=x1;
x[
2]:=x2;
x[
3]:=x3;
x[
4]:=x4;
x[
5]:=x5;
x[
6]:=x6;
x[
7]:=x7;
x[
8]:=x8;
x[
9]:=x9;
sum:
=0;
if f[x1,x2,x3,x4,x5,x6,x7,x8,x9]>=0 then
exit(f[x1,x2,x3,x4,x5,x6,x7,x8,x9]);
f[x1,x2,x3,x4,x5,x6,x7,x8,x9]:
=0;
for i:=1 to 9 do
for j:=i+1 to 9 do
if (a[i,x[i]]=a[j,x[j]])and(x[i]<>0)and(x[j]<>0) then
begin
dec(x[i]);dec(x[j]);
inc(sum);
f[x1,x2,x3,x4,x5,x6,x7,x8,x9]:
=f[x1,x2,x3,x4,x5,x6,x7,x8,x9]+find(x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9]);
inc(x[i]);inc(x[j]);
end;
if sum<>0 then
f[x1,x2,x3,x4,x5,x6,x7,x8,x9]:
=f[x1,x2,x3,x4,x5,x6,x7,x8,x9]/sum;
find:
=f[x1,x2,x3,x4,x5,x6,x7,x8,x9];
end;

begin
init;
fillchar(f,sizeof(f),
128);
f[
0,0,0,0,0,0,0,0,0]:=1;
f[
4,4,4,4,4,4,4,4,4]:=find(4,4,4,4,4,4,4,4,4);
writeln(f[
4,4,4,4,4,4,4,4,4]:0:6);
end.

反思

又是一个方程很多维的动态规划,只要能表示状态即可。根据数据范围能够发现是可行的。有些时候动态规划的递推关系比较哪找,可以采取记忆化搜索的思路。

注意边界

posted @ 2011-06-12 17:29  liukee  阅读(595)  评论(0编辑  收藏  举报