• 【[JLOI2013]卡牌游戏】


    思路太妙了

    刚开始yy出了一种比较自然的dp方法,就是按照游戏的进行来开始dp,设(dp[i][j])表示第(i)个人为庄家,还剩下(j)个人的概率为多少,但是很快发现这个样子没法转移,因为没有办法确定下一个庄家是谁

    于是只能将第二维压成一个状态(s) ,(dp[i][s])表示第(i)个人为庄家存活状态为(s)的概率为多少,显然这个样子做一个状压dp的话我们可以枚举当前的庄家,当前的状态,以及当前的庄家用的牌是哪一张,之后我们就可以确定下一个庄家是谁,这样就可以转移了

    但是这个复杂度大概是(O(nm2^n)),30%的数据应该还是能过的

    正解的思路就相当秒了,我们设(dp[i][j])表示有(i)个人参与游戏,从庄家(即1)数(j)个人获胜的概率是多少

    初始状态(dp[1][1]=1)

    之后我们枚举(i,j),之后继续枚举(k)表示庄家(即1)选择了第(k)张牌,由于我们这里的庄家都是1所以我们要通过模拟第一步进行状态的转移

    我们的到第一步之后就可以转化为(dp[i-1][])

    我们枚举了(k)自然就可以得到第一轮被淘汰的人(p)

    如果(p=j)(j)上来就被淘汰就不用转移了

    而第 (p)个人被淘汰之后,剩下的 (i-1) 个人要组成一个新的环,庄家为第 (p)个人的下一个。容易算出,当 (p>j) 时,第 (j)个人是新的环里从新庄家数起的第 (i-p+j) 个人,当 (c<j) 时,第 (j) 个人是新的环里从新庄家数起的第 (j-p)个人。

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define re register
    #define maxn 55
    int n,m;
    int a[maxn];
    double dp[maxn][maxn];
    inline int read()
    {
    	char c=getchar();
    	int x=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9')
    	  x=(x<<3)+(x<<1)+c-48,c=getchar();
    	return x;
    }
    int main()
    {
    	n=read(),m=read();
    	for(re int i=1;i<=m;i++)
    		a[i]=read();
    	dp[1][1]=1.0;
    	for(re int i=2;i<=n;i++)
    	{
    		for(re int j=1;j<=i;j++)
    		{
    			for(re int k=1;k<=m;k++)
    			{
    				int p=a[k]%i;
    				if(!p) p=i;
    				if(p>j) dp[i][j]+=dp[i-1][i-p+j]/m;
    				if(p<j) dp[i][j]+=dp[i-1][j-p]/m;
    			}
    		}
    	}
    	for(re int i=1;i<=n;i++)
    	printf("%.2lf",dp[n][i]*100),putchar('%'),putchar(' ');
    	return 0;
    }
    
  • 相关阅读:
    codevs 1576 最长严格上升子序列
    codevs 3415 最小和
    codevs 2102 石子归并 2
    洛谷 P1040 加分二叉树
    BZOJ 3038 上帝造题的七分钟二
    codevs 线段树练习ⅠⅡⅢ
    启动Tomcat提示:指定的服务未安装
    poj 1061 青蛙的约会 (扩展欧几里得模板)
    POJ 3449 Geometric Shapes(判断几个不同图形的相交,线段相交判断)
    HDU 5251 矩形面积(二维凸包旋转卡壳最小矩形覆盖问题) --2015年百度之星程序设计大赛
  • 原文地址:https://www.cnblogs.com/asuldb/p/10207767.html
Copyright © 2020-2023  润新知