Hdu 5245 Joyful【滚动数组+概率Dp】

本文探讨了一个关于随机涂色的问题,给出了详细的算法思路和实现代码。通过定义状态转移方程来计算期望被涂色的点的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Joyful

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1413    Accepted Submission(s): 622


Problem Description
Sakura has a very magical tool to paint walls. One day, kAc asked Sakura to paint a wall that looks like an M×N matrix. The wall has M×N squares in all. In the whole problem we denotes (x,y) to be the square at the x-th row, y-th column. Once Sakura has determined two squares (x1,y1) and (x2,y2), she can use the magical tool to paint all the squares in the sub-matrix which has the given two squares as corners.

However, Sakura is a very naughty girl, so she just randomly uses the tool for K times. More specifically, each time for Sakura to use that tool, she just randomly picks two squares from all the M×N squares, with equal probability. Now, kAc wants to know the expected number of squares that will be painted eventually.
 

Input
The first line contains an integer T(T100), denoting the number of test cases.

For each test case, there is only one line, with three integers M,N and K.
It is guaranteed that 1M,N5001K20.
 

Output
For each test case, output ''Case #t:'' to represent the t-th case, and then output the expected number of squares that will be painted. Round to integers.
 

Sample Input
2 3 3 1 4 4 2
 

Sample Output
Case #1: 4 Case #2: 8
Hint
The precise answer in the first test case is about 3.56790123.

题目大意:


给你一个N*M的矩阵,进行K次随机操作,每个点被选中的概率都是等同的,每次操作随机选两个点,作为一个矩阵的对角,然后将这个矩阵涂上颜色,问K次操作之后,期望被涂上色的点的个数。点可以被重复涂抹。


思路:


我们没法考虑最终的形状,要不然时间复杂度非常大,所以我们不妨考虑一个点在K次操作之后是否会贡献其本身的价值(即这个点在k次操作之后是否被涂抹了)。


那么设定Dp【i】【x】【y】【2】:

①Dp【i】【x】【y】【0】表示操作了i次之后,(x,y)这个点没有被涂的概率。

②Dp【i】【x】【y】【1】表示操作了i次之后,(x,y)这个点被涂了的概率。


那么状态转移方程并不难写出,考虑到double类型的数据,O(n*m*k)的空间复杂度有些大(实际最开始第一发就交的这个,然后MLE了之后才改的)。我们滚动一下数组再写出状态转移方程有:

这里P【i】【j】表示涂抹一次,(i,j)这个点被涂抹到的概率。


其计算方式也很简单,简单容斥几个公式就行了。这里具体参考一下Ac代码吧。



那么ans=ΣDp【n】【x】【y】【1】;


Ac代码(800+ms过的):


#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
#define ll long long int
double p[501][501];
double dp[2][501][501][3];
ll Cal(ll a,ll b)
{
    return a*a*b*b;
}
int main()
{
    int kase=0;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll n,m,k;
        memset(dp,0,sizeof(dp));
        scanf("%lld%lld%lld",&n,&m,&k);
        for(ll i=1;i<=n;i++)
        {
            for(ll j=1;j<=m;j++)
            {
                p[i][j]+=Cal(i-1,m);
                p[i][j]+=Cal(n-i,m);
                p[i][j]+=Cal(n,j-1);
                p[i][j]+=Cal(n,m-j);
                p[i][j]-=Cal(i-1,j-1);
                p[i][j]-=Cal(i-1,m-j);
                p[i][j]-=Cal(n-i,j-1);
                p[i][j]-=Cal(n-i,m-j);
                double fenmu=n*m*n*m;
                p[i][j]/=fenmu;
                p[i][j]=1-p[i][j];
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                dp[0][i][j][0]=1.0;
            }
        }
        for(int z=1;z<=k;z++)
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    dp[1][i][j][0]+=dp[0][i][j][0]*(1.0-p[i][j]);
                    dp[1][i][j][1]+=dp[0][i][j][0]*(p[i][j]);
                    dp[1][i][j][1]+=dp[0][i][j][1]*(p[i][j]);
                    dp[1][i][j][1]+=dp[0][i][j][1]*(1.0-p[i][j]);
                    dp[0][i][j][0]=dp[1][i][j][0];
                    dp[0][i][j][1]=dp[1][i][j][1];
                    dp[1][i][j][0]=0;
                    dp[1][i][j][1]=0;
                }
            }
        }
        double output=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                output+=dp[0][i][j][1];
            }
        }
        printf("Case #%d: ",++kase);
        printf("%.0f\n",output);
    }
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值