日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最大子矩阵(普通和01)

發布時間:2023/12/3 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最大子矩阵(普通和01) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 普通矩陣(單個矩陣值為任何數)
      • 最大子段和
      • 擴展到二維情況
    • 01矩陣(單個矩陣值為0或1)
    • 代碼:

普通矩陣(單個矩陣值為任何數)

例題:POJ 1074

求出其中最大的子矩陣
答案是:
9 2
-4 1
-1 8
最大和是15
我們先想想如果不是矩陣,是一個數組,求其中連續的最長一段,咋做?

最大子段和

我們用b[i]來表示a[0]…a[1]的最大子段和
那么b [ i ] =max ( b [i - 1 ] + a [ i ], a [ i ] )
當a[n] 大于dp[n-1](前n-1個元素序列的最大子段和)。此時舍棄前面的子段和,即斷開。
否則的話,把a[n]連接到前n-1個元素序列的最大和子段上,即dp[n] = dp[n-1] + a[n]

int MaxSum(int n,int *a) {int sum=0,b=0;for(int i=0;i<n;i++){if(b>0) sum=max(sum,b+=a[i]);//不要忘了把b更新else sum=max(b=a[i],sum);}return sum; }

擴展到二維情況

二維矩陣排列方式如圖
參考題解

如果紅色是我們要找的最大子矩陣
那和就是:(a[q][i]+…+a[p][i],a[q][i+1]+…+a[p][i+1],…,a[q][j]+…a[p][j])=(sum1,sum2,…,sumn
sum也就是每一列的相同行數相加
這不也是sun的一維數組,那其實就是一個一維數組的最大子段問題
如果把二維數組看成是縱向的一維數組和橫向的一維數組

#include <iostream> #include <cstring> using namespace std;int maxsub(int a[], int n) {int i, max = a[0], b = 0;for (i = 0; i < n; i++){if (b + a[i] >= a[i]) //當n-1的最大字段和+a[i]>=原來的最大字段和b += a[i]; //則n的最大字段和為b+a[i]else //否則,就是a[i]b = a[i];if (b > max) //找到最大的最大字段和max = b;}return max; }int main() {int n, i, j, k, maxsubrec, maxsubarr, m;int dp[101][101], arr[101];cin >> n >> m;for (i = 0; i < n; i++)for (j = 0; j < m; j++)cin >> dp[i][j];maxsubrec = dp[0][0];for (i = 0; i < n; i++) //i用于標識從哪行開始,依次選擇行{memset(arr, 0, sizeof(arr));for (j = i; j < n; j++) //從第i行開始,j為選擇幾行{for (k = 0; k < m; k++) //從1列選擇到m列,壓縮行arr[k] += dp[j][k];//arr表示每列的值maxsubarr = maxsub(arr, m); //從每一個壓縮行中選擇最大字段和if (maxsubarr > maxsubrec) maxsubrec = maxsubarr;}}cout << maxsubrec << endl;return 0; }

01矩陣(單個矩陣值為0或1)

參考題解
POJ - 3494

其實就是沒有負數情況
我一開始想的是將最大子矩陣的代碼中,如果一個數為0就賦值為-10000(這樣程序就肯定不會選擇這個塊)
但是這樣會超時
針對01矩陣我們可以這么想:
每一個單元格的值等于它所在列的連續1的數量,如果當前行為1,則當前行的值就等于上一行加1,如果當前行為0,則他的值也是0
我們掃描每一行,求出這一行中可形成的矩形最大面積,然后去最大情況即可
然后我們要用到一個單調遞減的單調棧,
如果棧為空或者當前元素大于等于棧頂元素,則入棧
若棧非空且當前元素小于棧頂元素,則將棧頂彈出,更新面積最大值,直到棧空或者遇到第一個大于等于當前元素
將最后一個出棧的棧頂元素(最后一個大于當前元素的)向左右延伸,修改其值,并入棧。
核心都是對于每個高度為h的矩形,找到最左高度大于等于它的位置,和最右的位置。

我們看第三行:
0 1 3 2 0 0 0
0先入棧,然后1入棧,然后3入棧,然后到2時,2<3,計算此時的矩陣面積=(4-3) * 3=3,此時最大面積是3,然后將值3改為2,此時就是0 1 2 2 ,然后0<2,計算此時面積 = (5-3) * 2=4,最大面積就是4,一次類推,都是按照這個操作

代碼:

#include<stdio.h> #include<string.h> #include<iostream> #include<stack> using namespace std;int main() {//top指向棧頂;tmp為臨時變量,記錄面積的值;ans為答案,記錄面積的最大值 int i,j,m,n,x,top,tmp,ans,h[2020],a[2020];stack<int> st; //單調棧,記錄位置 while(~scanf("%d%d",&m,&n)){ans=0;memset(h,0,sizeof(h)); //用于第一行的處理for(i=0;i<m;i++){ //掃描每一行 for(j=1;j<=n;j++){scanf("%d",&x);if(x==1) h[j]=h[j]+1; //如果是1,則在上一行本列的基礎上+1 else h[j]=0; //否則為0 a[j]=h[j]; //a數組用來向左右擴展}a[n+1]=-1; //設最后元素為最小值,以最后讓棧內元素出棧 for(j=1;j<=n+1;j++){if(st.empty()||a[j]>=a[st.top()]){ //如果棧為空或入棧元素大于等于棧頂元素,則入棧 st.push(j);}else{while(!st.empty()&&a[j]<a[st.top()]){ //如果棧非空并且入棧元素小于棧頂元素,則將棧頂元素出棧 top=st.top();st.pop();tmp=(j-top)*a[top]; //計算面積值 if(tmp>ans) ans=tmp; //更新面積最大值 }st.push(top); //將最后一次出棧的棧頂元素延伸并入棧 a[top]=a[j]; //修改其對應的值 }}}printf("%d\n",ans);}return 0; }

總結

以上是生活随笔為你收集整理的最大子矩阵(普通和01)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。