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)
题意:
给你一个矩阵, 求
因为说数字太抽象了,所以我们把不同数字定义为不同颜色。
拿样例来说
1 2 1
2 1 2
1是黄色,2是绿色
记子矩阵大小为size ,子矩阵*包含的颜色个数记为sum
size(1)=6 每个矩阵只有1种颜色,sum(1)=6*1=6;
size(2)=7(①②,②③,④⑤,⑤⑥,①④,②⑤,③⑥),每个矩阵只有2种颜色,sum(2)=7*2=14;
依次类推,最终
想法:
1.求子矩阵个数
求法1:
二重循环枚举子矩形右下角的点(i,j),那么从前i行中找一行作为矩阵的上边,从前j列中找一列作为矩阵的左列,所以形成的矩阵共
求法2:
构成矩阵需要两条竖边。两条横边,所以运用组合数,得
2.求不同颜色的矩阵数
在求size(4)时,当以①为此时遍历的点时,我们选择①②③④,在遍历②时,就不能再选择这个矩阵了。为了避免这种重复的情况,我们选择一个方向比如从上到下从左往右遍历。
下面我们以栗子来具体讲解怎么计算不同颜色矩阵数,如图,10*10的矩阵:
我们以计算1种颜色为例(其他多种颜色即可看成不是这种颜色的颜色,所以棋盘里只有 存在与不存在这种颜色)
黄色是已经遍历过计算出包含这种颜色矩阵的个数,蓝色是我们此时正在遍历的点,绿色是未遍历的点。
下面我们计算包含蓝点的区域。
(1). 第三列第十列第六行直至最后一行构成的区间,所以我们只要在第4列的左边取一条边,右边取一条边就能构成一个矩阵,即左边界l=3,右边界r=10。
注意:这里l≠1,r=m,因为以第1列为左边,第4列为右边,第6行为上边的矩阵可能在遍历⑩时已经遍历过了,所以我们要判断这一行之前是否有同色的以更改左边。
(2)蓝点以上的空白区域,比如以第3列为左边,第7列为右边,第4行为上边的矩阵。所以我们可以一行行的,并且左右边不断向里缩小的寻找空白区域。
注意:假设上边已经减到第五行时,右边界会减小到第八列;之后行数向上减小,右边界只会再次向左减小,所以我们发现④在此时没有什么用处。因此同一列只有最下面一个是有用的,这在优化中能体现出来(若遍历点的上面有同色的直接跳出循环)。
综上我们发现,是要通过确定各边界来确定矩阵的个数的,而改变边界就是上面思路里的想法来实现。
所以,愉快的敲代码辣~
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int col[110][110];
int n,m,t,r,l;
int solve(int x,int y)
{
long long sum=0;
int l=1,r=m;
int nowcol=col[x][y];
for(int i=x; i>=1; --i)
{
if(i<x&&col[i][y]==nowcol)break;
else
{
for(int j=y-1; j>=max(l,1); --j)
{//确定左边界
if(col[i][j]==nowcol)
{
l=max(l,j+1);
break;
}
}
if(i==x)
{
sum+=(n-x+1)*(y-l+1)*(r-y+1);
continue;
}
else
{
for(int j=y+1; j<=min(r,m); ++j)
{//确定右边界
if(col[i][j]==nowcol)
{
r=min(j-1,r);
break;
}
}
}
sum+=(n-x+1)*(r-y+1)*(y-l+1);
}
}
return sum;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(col,0,sizeof col);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=m; ++j)
scanf("%d",&col[i][j]);
}
long long numval=0,num=0;
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=m; ++j)
{
numval+=solve(i,j);
num+=i*j;
}
}
printf("%.9lf\n",numval*1.0/num);
}
return 0;
}
ps:哇 这个题搞死我QAQ 一堆break continue的:)