TopCoder[SRM513 DIV 1]:PerfectMemory(500)

Problem Statement

     You might have played the game called Memoria. In this game, there is a board consisting of N rows containing M cells each. Each of the cells has a symbol on its back. Each symbol occurs on exactly two cells on the board. 



A move means turning a pair of cells one by one to see the symbols behind them. When the symbols differ, both of the cells are turned on their faces, thus hiding the symbols again. The player should remember the symbols. If the symbols on the backs of the turned cells coincide, the cells stay that way, i.e., don't turn back again. As soon as after some move all the cells on the board are turned (such that all the symbols are simultaneously visible), the game ends. 



Manao has a perfect memory, so when he sees the symbol behind some cell, he remembers it forever. Manao is trying to finish the game in the least expected number of moves. Find the expected number of moves he will need to accomplish this.

Definition

    
Class: PerfectMemory
Method: getExpectation
Parameters: int, int
Returns: double
Method signature: double getExpectation(int N, int M)
(be sure your method is public)

Limits

    
Time limit (s): 2.000
Memory limit (MB): 64

Notes

- The board Manao plays on is generated as follows. The same set of (N * M) / 2 symbols is used for each generation. The board contents are chosen uniformly among all valid N x M boards.
- The returned value must have an absolute or relative error less than 1e-9.

Constraints

- N will be between 1 and 50, inclusive.
- M will be between 1 and 50, inclusive.
- N * M will be even.

Examples

0)  
    
1
2
Returns: 1.0
There are only two cells on the board, so the game always ends in one move.
1)  
    
2
2
Returns: 2.6666666666666665
There are four cells. The game may flow in two possible scenarios:

1) In the first move, Manao turns two cells with equal symbols. The game ends in two moves then and the probability of such a first move is 1/3.

2) In the first move, Manao turns two cells with different symbols. Then he finishes the game in three moves and the probability of such a first move is 2/3.

The overall expected number of moves is 1/3 * 2 + 2/3 * 3 = 8/3.
2)  
    
2
3
Returns: 4.333333333333334
 
3)  
    
4
4
Returns: 12.392984792984793
 

题意:你有n*m张卡片,总共n*m/2种,每种有两张。每次操作时你可以翻开两张,若一样,则它们不再翻回去;否则,这两张卡面会向你展示后再翻回去(不改变位置),假设你记忆力很好,能够记住牌的种类。求出至少进行多少次操作,可以使所有牌都同时正面朝上。注意,一次操作翻牌是有先后的,即你可以先翻一张牌,看过其种类后,再决定翻第二张牌。

 

题解:

假设翻到一样的牌,它们就会被消掉。dp[i][j]表示场上还有i+j张牌未被消掉,已经翻开过其中i张牌(即知道其种类),剩下的j张牌还未被翻开过。

每次操作一定会先翻一张未被翻过的牌,若其与i张牌中的一张对应,则第二张牌一定翻这张,把它们消掉。若是一张新的牌,则第二张牌一定也翻未被翻过的牌。

计算每种情况出现的概率与期望,以j为阶段、i为状态,进行动态规划。

 

代码:

 1 double dp[2502][2502];
 2 class PerfectMemory 
 3 {
 4     public:
 5     double getExpectation(int N, int M) 
 6     {
 7         //$CARETPOSITION$
 8         int n=N*M; dp[0][0]=0;
 9         for(int j=0;j<=n;j++)
10         for(int i=0;i<=n;i++)
11         {
12             if((i==0)and(j==0))continue; dp[i][j]=10000000;
13             if(i>j)continue; if(i+j>n)continue; if((j-i)%2==1)continue;
14             dp[i][j]=0; double p,p2,a1;
15             p=i; p=p/j; p2=1-p;
16             if((j>0)and(i>0))
17             {
18                 dp[i][j]=dp[i][j]+p*(1+dp[i-1][j-1]);
19             }
20             double b,c,d;
21             if(j>1)
22             {
23                 b=1; b=b/(j-1); c=j-2-i; c=c/(j-1); d=1-b-c;
24                 a1=b*(1+dp[i][j-2])+c*(1+dp[i+2][j-2])+d*(2+dp[i][j-2]);
25                 dp[i][j]=dp[i][j]+p2*a1;
26             }
27         }
28         return dp[0][n];
29         
30     }
31 };
View Code
posted @ 2017-04-05 14:26  GhoStreach  阅读(305)  评论(0编辑  收藏  举报