题目:
http://acm.hdu.edu.cn/showproblem.php?pid=6052
题意:矩阵上每个点有不同的颜色,求矩阵的子矩阵中不同颜色个数的期望
考虑每个颜色的贡献,即每个颜色只能对一个子矩阵贡献一次,从颜色的角度来考虑,考虑一个顺序,每个点只能和其颜色相同的并且位置在其左上角的点冲突,这样就可以保证不重复了,然后直接暴力就可以了
#include<bits/stdc++.h>
using namespace std;
int a[105][105];
int book[10005][105];
int main()
{
int T,n,m,i,j,k;
cin>>T;
while(T--)
{
cin>>n>>m;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
book[a[i][j]][i]=1;//标记了这一行有没有此颜色出现
}
}
double ans=0;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
int now=a[i][j];
int up=n,down=i,le=1,ri=m;
for(k=j-1;k>=1;k--)
{
if(a[i][k]==now)
{
le=k+1;
break;
}
}
ans+=(up-down+1)*(j-le+1)*(ri-j+1);//上边界为此行时,不需要考虑右边的点
// cout<<i<<" "<<j<<" "<<le<<" "<<ri<<" "<<down<<" "<<up<<" "<<ans<<endl;
for(down=i-1;down>=1;down--)//向上枚举上边界,不断修改左右可到的边界,范围只可能越来越小,
{
if(book[now][down])//如果此行没有此颜色出现,则不需要修改左右的边界,一个小优化,不加也能过(500ms)加了(90ms)
{
for(k=j;k>=le;k--)
{
if(a[down][k]==now)
{
le=k+1;
break;
}
}
for(k=j;k<=ri;k++)
{
if(a[down][k]==now)
{
ri=k-1;
break;
}
}
}
if(le>ri)
break;
// cout<<i<<" "<<j<<" "<<le<<" "<<ri<<" "<<down<<" "<<up<<" "<<ans<<endl;
ans+=(j-le+1)*(ri-j+1)*(up-i+1);//上边界已确定,并且矩阵必须包含原点
}
}
}
ans/=(n+1)*n*(m+1)*m/4.0;
printf("%.9lf\n",ans);
memset(book,0,sizeof(book));
}
return 0;
}