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的:)

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