hdu 6052 To my boyfriend 暴力枚举+容斥去重

来源:互联网 发布:三国无双Mac版 编辑:程序博客网 时间:2024/06/05 15:50
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
12 31 2 12 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)
题意:
给出一个n*m(1<=n,m<=100)的矩阵,每个矩阵元素有一个颜色值ai(0<=ai<=10000),现在定义一个子矩阵的value为子矩阵中不同颜色的数量,求子矩阵value的期望。
思路:
数学期望=所有矩阵value数值之和/矩阵的个数
首先解决矩阵的个数问题,直接枚举(i,j)以(i、j)为右下角的矩阵个数为i*j个,这样就能得到所有矩阵的个数
然后需要解决的是所有矩阵的value值,直接暴力找每个矩阵中不同颜色的数量肯定是不行的,所以我们可以换个思路,我们可以枚举每个位置,然后计算一下该位置上的颜色对结果的贡献,但是由于每个位置的颜色不是独一无二的,有的位置的颜色是一样的,这样的话我们需要采用容斥原理去重,但是这个题目中只要按照一定的规则枚举就能避免重复的情况,我们选择从下到上,从左到右的规则枚举,当我们枚举到某个位置时,我们需要确定这个位置的颜色所在矩阵的上边界和左右边界 (以枚举的位置为其下边界),具体确定方法看代码注释
ac代码:
#include <bits/stdc++.h>using namespace std;typedef long long LL;const int MAXN = 105;int n, m;int color[MAXN][MAXN];int cal(int x, int y){    LL res = 0;    int c = color[x][y], L = 1, R = m;    for (int i = x; i >= 1; i--)//枚举矩阵的上限    {        if (i < x && color[i][y] == c) break;//如果碰到上面有相同颜色的时候就应该结束了,这个颜色独立存在一个矩阵中的上限到了        int l = y, r = y;        for (int j = y - 1; j >= max(1, L); j--)//确定好上下限,开始确定左边的限制        {            if (color[i][j] == c) break;            l = j;        }        L = max(L, l);        if (i == x)//如果上限x的时候,右边的权限不用考虑,因为当枚举到后边时会有相应的左限,不会重复(与我们的查找策略相对应,从左到右)        {            res += (LL)(n-x+ 1LL) * (y - L + 1LL) * (R - y + 1LL);            continue;        }        for (int j = y + 1; j <= min(m, R); j++)//确定右边界        {            if (color[i][j] == c) break;            r = j;        }        R = min(R, r);        res += (LL)(n-x+ 1LL) * (y - L + 1LL) * (R - y + 1LL);//(下边界方案*左边界方案*右边界方案,上边界已经确定是xi)    }    return res;}int main(){    int T;    scanf("%d", &T);    while(T--)    {        scanf("%d%d", &n, &m);        for (int i = 1; i <= n; i++)        {            for (int j = 1; j <= m; j++)            scanf("%d", &color[i][j]);        }        LL gg = 0, ss = 0;        for (int i = 1; i <= n; i++)        {            for (int j = 1; j <= m; j++)            {                gg += cal(i, j);                ss += i * j;            }        }        printf("%.9f\n", gg * 1.0 / ss);    }    return 0;}