bzoj1084: [scoi2005]最大子矩陣
這裡有乙個n*m的矩陣,請你選出其中k個子矩陣,使得這個k個子矩陣分值之和最大。注意:選出的k個子矩陣不能相互重疊。
第一行為n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下來n行描述矩陣每行中的每個元素的分值(每個元素的分值的絕對值不超過32767)。
只有一行為k個子矩陣分值之和最大為多少。
3 2 2
1 -3
2 3-2 3
9最大子矩陣嘛。。。
當然$dp$。。。
然後看見$m\in[1,2]$,果斷分類討論:
普通的最大連續欄位和,只不過是$k$個:
設$dp[i][j][0/1]$表示前$i$個數中取出$j$個矩形的最大和。
$0/1$表示第$i$gre數字不選或選。
轉移方程:
$$dp[i][j][0]=\max\left\{}dp[i-1][j][1]\\dp[i-1][j][0]\end\right.$$
$$dp[i][j][1]=\max\left\{}dp[i-1][j][1]\\dp[i-1][j-1][0]\end\right.$$
最後答案就是$\max\$。
我們考慮每一行能有什麼狀態:
$0$:空出這一行。
$1$:選擇左邊空出右邊.
$2$:選擇右邊空出左邊。
$3$:選擇這一行兩個,並且不作為乙個矩陣,而是左邊一列單獨乙個矩陣,右邊單獨乙個矩陣。
$4$:選擇這一行兩個,並且兩個一塊作為乙個矩陣的一部分。
定義$dp[i][j][k]$為當前處理到第$i$行,已經選了$j$個矩陣,當前行狀態為$k$的最大值,$k\in[0,4]$。
$$dp[i][j][0]=\max\left\{}dp[i-1][j][0]\\dp[i-1][j][1]\\dp[i-1][j][2]\\dp[i-1][j][3]\\dp[i-1][j][4]\end\right.$$
2. 如果選擇左邊空出右邊:
如果上一行的左邊沒有單獨地選擇成為矩陣,即選擇$1$或$3$,則$j$需要包含新選擇成為的矩陣。
即這一行的左邊的這個矩陣。
如果上一行為同時選擇兩列的為乙個矩陣的狀態,則只選擇單獨的左邊是不能包含進去上一行的矩陣的,所以也應$j-1$。
設這一行左邊的值為$v_1$,則有轉移方程:
$$dp[i][j][1]=\max\left\{}dp[i-1][j-1][0]\\dp[i-1][j][1]\\dp[i-1][j-1][2]\\dp[i-1][j][3]\\dp[i-1][j-1][4]\end\right\}+v_1$$
3. 右邊同理:
設這一行右邊的值為$v_2$,則有轉移方程:
$$dp[i][j][2]=\max\left\{}dp[i-1][j-1][0]\\dp[i-1][j-1][1]\\dp[i-1][j][2]\\dp[i-1][j][3]\\dp[i-1][j-1][4]\end\right\}+v_2$$
4. 選擇兩個分別單獨作為矩陣,類似只選擇左邊或右邊,不過是單獨選左邊和右邊合併了下:
$$dp[i][j][3]=\max\left\{}dp[i-1][j-1][1]\\dp[i-1][j-1][2]\\dp[i-1][j][3]\\dp[i-1][j-2][4]\quad j\geq 2\end\right\}+v_1+v_2$$
$$dp[i][j][4]=\max\left\{}dp[i-1][j-1][0]\\dp[i-1][j-1][1]\\dp[i-1][j-1][2]\\dp[i-1][j-1][3]\\dp[i-1][j][4]\end\right\}+v_1+v_2$$
好了,這個題就這麼做完了。
真是噁心啊。。。
然後這個題如果資料範圍再大一點,涉及到卡常的話,有個小優化:
當我們的程式要多次呼叫$dp[i][j][k]$並且只有$k$在瘋狂變化時,我們可以預先把$dp[i][j]$當成指標存入*$p$中。
這樣我們就不用每次都計算三維的$dp[i][j][k]$,而是直接呼叫$p[k]$。
這個優化可以瘋狂卡常。。。
因為$c++$中查詢乙個多維的陣列,要先進行複雜的乘法計算,然後再取位址,呼叫值。
這個優化直接把這個每次計算省掉了。
不過對於這個題好像沒有什麼用。。。
附**:
#include#include#include#include#define maxn 110using namespace std;
int n,m,q,ans;
int val[maxn][3],dp[maxn][12][5];
inline int read()
while(c>='0'&&c<='9')
return date*w;
}void solve_one()
ans=max(dp[n][q][0],dp[n][q][1]);
printf("%d\n",ans);
}void solve_two()
ans=max(dp[n][q][0],max(max(dp[n][q][1],dp[n][q][2]),max(dp[n][q][3],dp[n][q][4])));
printf("%d\n",ans);
}void work()
void init()
int main()
bzoj1084 SCOI2005 最大子矩陣
description 這裡有乙個n m的矩陣,請你選出其中k個子矩陣,使得這個k個子矩陣分值之和最大。注意 選出的k個子矩陣 不能相互重疊。input 第一行為n,m,k 1 n 100,1 m 2,1 k 10 接下來n行描述矩陣每行中的每個元素的分值 每個元素的 分值的絕對值不超過32767 ...
BZOJ1084 SCOI2005 最大子矩陣
portal 注意到m只能為1或2 分類討論。m 1的時候其實就是最大k段連續子段和。f i j ma x f i j ma x f k j f k j 1 sum i s um k m 2時 g i j k 表示第一列到i,第二列到j,選了k個子矩陣的最大和 那麼有一下幾種情況 都不選 g i j...
BZOJ1084 SCOI2005 最大子矩陣
這題顯然是dp。定義f i j k 表示前i行j列裡有k個矩陣的最大元素總和。因為m 2,所以可以分兩種情況分別寫乙個dp,套一套容斥就行了。如下 include include include using namespace std const int maxn 105,maxk 15 int n...