TopCoder[SRM513 div 1]:(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 };

时间: 2024-08-29 19:59:19

TopCoder[SRM513 div 1]:(500)的相关文章

Topcoder Srm627 DIV 2

A,B:很水,注意边界,话说HACK都是这些原因. C: R[I][J]:表示反转I-J能改变冒泡排序的次数: DP方程:dp[i][k]=max(dp[j][k],dp[j][k-1]+dp[j][i])  (0<=j<i) 最后枚举,具体看代码 #include<stdio.h> #include<iostream> #include<vector> #include<algorithm> #include<cmath> #inc

Topcoder SRM 619 DIv2 500 --又是耻辱的一题

这题明明是一个简单的类似约瑟夫环的问题,但是由于细节问题迟迟不能得到正确结果,结果比赛完几分钟才改对..耻辱. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define ll long long using namespace std; #define NN 370000 class Choo

Topcoder SRM 687 (Div 2) 500.Quacking

Problem Statement   Ducks have started mysteriously appearing in your room. All ducks make the same sound: "quack". Each duck makes the sound one or more times, one after another. For example, valid sounds for a single duck are "quack"

TopCoder SRM 639 Div.2 500 AliceGameEasy

题意: 一个游戏有n轮,有A和B比赛,谁在第 i 轮得胜,就获得 i 分,给出x,y,问A得x分,B得y分有没有可能,如果有,输出A最少赢的盘数 解题思路: 首先判断n(n+1)/2 = (x+y)是否有解,即是否存在n为整数,使得所有分数的和加起来为x+y,即判断n2+n-2(x+y)=0,存在整数解, 根据二次方程的根为(-1±Δ)/2 为整数,其中Δ=√(1+8(x+y)) , 即判断1+8(x+y)是否能开方以及Δ是否为奇数(如果Δ为偶数,则根不是整数) 如果前面条件满足,在通过贪心,从

TopCoder SRM 639 Div.2 500 AliceGameEasy --乱搞

题意: 一个游戏有n轮,有A和B比赛,谁在第 i 轮得胜,就获得 i 分,给出x,y,问A得x分,B得y分有没有可能,如果有,输出A最少赢的盘数. 解法: 这题是我傻逼了,处理上各种不优越,要使n*(n+1)/2 >= 10^12, n为10^6是不够的,要开大一点,总是细节地方不注意. 做法很简单,先用map或者其他什么东西判断x+y是否为某个n*(n+1)/2, 如果不是,那肯定为-1,再就是x=0有可能要单独考虑,然后就是选一些数凑成x,由于要最少,那么从大的开始凑起,可以暴力地凑,也可以

TopCoder SRM 633 Div.2 500 Jumping

题意:给一个点(x,y),给一些步长delta1,delta2...deltaN,问从(0,0)严格按照步长走完N步后能否正好到达(x,y)点. 解法:其实就是判断这些线段和(0,0)-(x,y)这条线段能否构成一个多边(角?)形的问题,只需判断最长的边是否不大于于所有边长和的一半即可. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #inclu

求拓扑排序的数量,例题 topcoder srm 654 div2 500

周赛时遇到的一道比较有意思的题目: Problem Statement      There are N rooms in Maki's new house. The rooms are numbered from 0 to N-1. Some pairs of rooms are connected by bidirectional passages. The passages have the topology of a tree. That is, there are exactly N-

Topcoder SRM 655 DIV1 500 FoldingPaper2 递归 + 枚举

题意:给你一张长W,宽H 的纸,问你能不能者成给定的大小, 每一次折纸只能折成整数大小. 解题思路:递推+枚举   枚举给定大小的长和宽,然后套进 W,H中求最小值  , 折纸策略最优是每次折半. 解题代码: 1 // BEGIN CUT HERE 2 /* 3 4 */ 5 // END CUT HERE 6 #line 7 "FoldingPaper2.cpp" 7 #include <cstdlib> 8 #include <cctype> 9 #incl

【补解体报告】topcoder 634 DIV 2

A:应该是道语文题,注意边界就好: B:开始考虑的太复杂,没能够完全提取题目的思维. 但还是A了!我愚蠢的做法:二分答案加暴力枚举, 枚举的时候是完全模拟的,比如每次取得时候都是从大到小的去取,最后统计答案! 好吧!忽略这种SB做法,只是提供一种当你想不到的时候,一种暴力破解的思路! 看到的一种正解:我们每次可以取(N-1)个,而且不用加入答案中, 先上代码: for (int i=0;i<s.size();i++) sum+=s[i]; return max(0,sum-N*(m-1));很简