hdu--6052--To my boyfriend

本文探讨了一个关于矩阵的数学问题,即随机选择一个子矩阵时,其包含的不同数值的期望值是多少。通过详细的分析和算法设计,给出了计算该期望值的具体步骤。

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

To my boyfriend

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 667    Accepted Submission(s): 305

Problem Description

Dear Liao

I never forget the moment I met with you. You carefully asked me: "I have a very difficult problem. Can you teach me?". I replied with a smile, "of course". You replied:"Given a matrix, I randomly choose a sub-matrix, what is the expectation of the number of **different numbers** it contains?"

Sincerely yours,
Guo

Input

The first line of input contains an integer T(T≤8) indicating the number of test cases.
Each case contains two integers, n and m (1≤n, m≤100), the number of rows and the number of columns in the grid, respectively.
The next n lines each contain m integers. In particular, the j-th integer in the i-th of these rows contains g_i,j (0≤ g_i,j < n*m).

Output

Each case outputs a number that holds 9 decimal places.

Sample Input

   
1 2 3 1 2 1 2 1 2

Sample Output

   
1.666666667
Hint
6(size = 1) + 14(size = 2) + 4(size = 3) + 4(size = 4) + 2(size = 6) = 30 / 18 = 6(size = 1) + 7(size = 2) + 2(size = 3) + 2(size = 4) + 1(size = 6)
思路:
要求所有子矩阵的期望只需找到所有子矩阵中不同权重的数量和子矩阵的数量 ans = (所有子矩阵中不同权重的数量)/(子矩阵的数量);现在只有两个问题了:子矩阵数量和所有子矩阵中不同权重的数量 
子矩阵的数量:若有一个n*m的矩阵,则这个矩阵的子矩阵的个数为n*(n+1)m(m+1)/4;(神奇的公式)
  所有子矩阵中不同权重的数量:在解决这个问题上要找到每个子矩阵中有多少种颜色(权重),换而言之就是要找每个颜色(权重)包含在多少个不同的子矩阵中。现在我们的任务就是要不重复地计算出每个颜色包含在多少个子矩阵中。首先我们对每个颜色按先行优先在列优先的排序规则排序,在计算子矩阵时把每个子矩阵都算在排序后最先出现的哪一个点上。这样在计算地i的点所包含的矩阵时所求矩阵一定不能包含前1—i-1个点。
因为 在计算地i的点所包含的矩阵时所求矩阵一定不能包含前1—i-1个点。所以计算的时候就要考虑上方的情况。 枚举上界进行计算,根据上界的那一行的情况(有没有包含已经计算过的点)来更新左右边界,对于每一个枚举到的上界,对应的矩形个数就是(n-i+1)*(j-l+1)*(r-j+1)。(下边界*左边界*右边界);
优化:
画图不难看出,对于每一列,只要标记出这一列最下边的那个点,这一列上方的点就不用再计算。
代码:
 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include<bits/stdc++.h>
#define N  110
using  namespace std;

typedef pair< intint> P;
vector<P> col[N * N];  ///存储所有的点;
vector< int> yth[N]; ///保存找过了的点;
int ls[N * N], n, m;  ///临时存储上一次找过的点,让后放进yth里面;

int main()
{
     int T_T;
    cin >> T_T;
     while(T_T--)
    {
        scanf( "%d%d", &n, &m);
        memset(col,  0sizeof(col));
         long  long ans =  0, num =  0;
         for( int i =  1; i <= n; i++)
        {
             for( int j =  1; j <= m; j++)
            {
                 int x;
                scanf( "%d", &x);
                col[x].push_back(P(i, j));                   ///颜色分类存放
            }
        }
         for( int i =  0; i < m * n; i++)
        {
             ///枚举每一种颜色
             if (col[i].empty())
                 continue;
            sort(col[i].begin(), col[i].end());                  ///按照坐标对同种颜色的点排序
            memset(ls,  0sizeof(ls));
             for( int j =  0; j < col[i].size(); j++)
            {
                 ///算每一种颜色的贡献(经过该颜色的每一个矩形)

                 int l =  1, r = m;  ///左右边界置为最宽,再慢慢进行维护

                 bool flag =  1;                   ///初始化左右界

                 for( int k =  1; k <= m; k++)  ///只需要标记每一列最下边的,上边的清空,进行优化;
                    yth[k].clear();

                 for( int k =  1; k <= m; k++)                      ///把每列最底端的点对应的行标记上该列
                {
                     if (ls[k]) ///第k列的行为ls[k];
                        yth[ls[k]].push_back(k); ///这一行多一个点,不能被下一次包含;
                }
                 int x = col[i][j].first;  ///颜色为i的第j个点的坐标的行
                 int y = col[i][j].second;  ///颜色为i的第j个点的坐标的列
                 for( int k = x; k >  0 && flag; k--)   ///行减  枚举上界
                {

                     for( int p =  0; p < yth[k].size(); p++)   ///枚举该行标记过的点,维护左右边界
                    {
                         if (yth[k][p] < y)  ///第k行,第p个点的 列 坐标。
                            l = max(l, yth[k][p] +  1);

                         else  if(yth[k][p] > y)
                            r = min(r, yth[k][p] -  1);

                         else
                        {
                            flag =  0;
                             break;
                        }                    ///如果找到一个与当前点同列的已更新点则退出
                    }
                     if (!flag)
                         break;
                     // printf("%d %d %d    ",n-x+1,y-l+1,r-y+1);
                     //printf("%d***\n",(n-x+1)*(y-l+1)*(r-y+1));
                    ans += (n - x +  1) * (y - l +  1) * (r - y +  1);              ///统计当前上界对应方案数
                }
                ls[y] = x;  ///第y列的,最下边点的所在的行 x
                 // printf("*********************\n");
            }
        }
        num = n * (n +  1) * m * (m +  1) /  4;
        printf( "%.9f\n", ( double)ans / num);                     ///输出期望
    }
     return  0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值