您的位置:首页 > 其它

To my boyfriend

2017-08-03 10:52 429 查看

To my boyfriend

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 969    Accepted Submission(s): 446

[align=left]Problem Description[/align]
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
 

[align=left]Input[/align]
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).
 

[align=left]Output[/align]
Each case outputs a number that holds 9 decimal places.
 

[align=left]Sample Input[/align]

1
2 3
1 2 1
2 1 2

 

[align=left]Sample Output[/align]

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*1的小矩阵中都有一个数字,现在求大矩阵中所有的矩阵所包含的数字种类和,再除以所有的矩阵数,求数字的期望。

思路:

    我们假设一个数字代表一种颜色,便于下面描述。 

   首先介绍一种求大矩阵中所有矩阵个数的方法。

    一个n*m的矩阵,它所包含的所有矩阵个数为:

int total=0;
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
total=total+i*j;


     此公式代表,以(i,j)这个小矩阵块为右下角的矩阵共有多少个,即从前i行中找一行作为大矩阵的上边,从前j列中找一列作为大矩阵的左列,这样所形成的大矩阵有多少种,即C(i,1)*C(j,1)即i*j。遍历矩阵的每一个1*1小矩阵,就可得n*m的矩阵总个数。

     下面介绍如何求出大矩阵中所有的矩阵所包含的颜色种类和。我们以一种颜色为例,这种颜色对答案的贡献为,所有包含这种颜色的矩形个数。我们遍历这种颜色的每一个1*1小矩阵,找出包含这个小矩阵的所有矩阵,就可以得出这个小矩阵对答案的贡献,再遍历所有该颜色的小矩阵,相加就可以得出该颜色对答案的贡献。但是这样直接算会产生重复。所以我们探讨消除这种重复的方法。

     首先,我们遍历时找的是,有多少个矩阵包含这个小矩阵,也就是说,有多少个矩阵至少包含一种这样的颜色,现在我们只要保证每次遍历都不与之前的重复,我们就可以消除重复。所以我们定义一种模式,即遍历每一个1*1小矩阵时,我们只能包含这个小矩阵以下,以右的同颜色矩阵,而这个小矩阵以左,以上的同颜色矩阵不再包含,这样就可以实现每次计算时都不重复。这样我们就转化成为寻找边界,即为寻找遍历该小矩阵时,我们所能计算的矩阵的边界。我们在之前定义的方法的限制下,找到每一个小矩阵所形成的上边界,下边界,左边界,有边界,其中这四条边界每一条都是不固定的。其中,由于上边界和下边界的取值范围固定,(设该小矩阵的位置是(x,y),则上边界的取值范围是1到x,下边界的取值范围是x到n),所以我们主要是通过枚举上边界,去限制左右边界。如何找边界及找到边界如何计算详见代码注释。

下面贴上代码:(具体实现过程详见注释)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int MAXN=105;

int n, m;
int color[MAXN][MAXN];

int count(int x, int y)
{
LL ans=0;
int c=color[x][y]; //c是颜色号

int L=1,R=m; //左边界初始化为1,右边界为m
for (int i=x; i>=1; i--) //把每一次上边界确定为i,根据上边界限制左右边界
{
if (i<x && color[i][y]==c)
break;
//遍历到第i行,若i<x && color[i][y]==c则说明[x][y]的正上方有一个同颜色的矩阵
//此时无论我们怎样确定左右边界,由于下边界为x到n,我们所得出的矩阵都一定会包含[i][y]这个矩阵
//而根据记下记右,不记上不记左的原则,我们不应包含[i][y]这个小矩阵,所以直接break

int l=y,r=y; //左右边界均从当前列的位置左右移动
for (int j=y-1; j>=max(1,L); j--)
{
if (color[i][j]==c) //从当前列数向左找,找到i行第一个同颜色的方块,记录下左边界
break;
l=j;
}
L=max(L,l);

if (i==x) //如果i为当前行,(即上边界为x),则只需要找左边界,右边界为m(因为可以包括右边和下边的相同颜色方块)
{
ans=ans+(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) //从当前列数向右找,找到i行第一个同颜色的方块,记录下右边界
break;
r=j;
}
R=min(R,r);

ans=ans+(LL)(n-x+1LL)*(y-L+1LL)*(R-y+1LL);
//这个公式代表,从x到n中选取一行作为所选矩形的下边,所选矩形的上边为第i行
//从L到y中选一列作为所选矩形的左边,从y到R中选一列作为矩形的右边
//四条线所圈出的矩形就是所选矩形
}
return ans;
}

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 ask=0,total=0;
for (int i=1; i<=n; i++)
{
for (int j=1; j<=m; j++)
{
ask=ask+count(i,j);
total=total+i*j; //计算n*m矩阵有多少个小矩阵,简单算法
}
}
printf("%.9f\n",ask*1.0/total);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: