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)
题意:
给你一个矩阵, 求 子矩阵的数字不能完全相同
因为说数字太抽象了,所以我们把不同数字定义为不同颜色。

拿样例来说
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;
依次类推,最终sumsize 为我们所求的结果。

想法:

1.求子矩阵个数
求法1:
二重循环枚举子矩形右下角的点(i,j),那么从前i行中找一行作为矩阵的上边,从前j列中找一列作为矩阵的左列,所以形成的矩阵共C1i *C1j 即i*j种。
求法2:
构成矩阵需要两条竖边。两条横边,所以运用组合数,得C2m *C2n 种。
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的:)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章