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

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)
题意:
给出一个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; }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值