題目地址:https://www.luogu.com.cn/problem/P4850 題解原地址:https://createsj.blog.luogu.org/solution-p4850 ...
題目地址:https://www.luogu.com.cn/problem/P4850
題解原地址:https://createsj.blog.luogu.org/solution-p4850
既然大家都在用記憶化搜索,那我就來個 DP
題解吧。有了 O2
誰還會用 DP
?!
狀態轉移方程很容易推出來。設問題給出的矩陣為 \(\textbf A\),其有一子矩陣,左上角坐標為 \((x_1,y_1)\) ,右下角坐標為 \((x_2,y_2)\) ,那麼當只考慮這個子矩陣時,最優解為將該子矩陣切開後的兩個矩陣的最優解的最小值與該子矩陣的各元素之和,即
\[ f_{x_1,y_1,x_2,y_2}=min_{x_1,y_1,x_2,y_2}+\sum\limits_{i=x_1}{x_2}\sum\limits_{j=y_1}^{y_2}\textbf A(i,j). \]
這個很容易用記憶化搜索實現,但應如何 DP
呢?
容易想到,我們應該枚舉每個行數和列數不都為 \(1\) 的子矩陣,並保證該子矩陣的任意子矩陣都已得出最優解。為了方便實現,我們設一子矩陣左上角坐標為 \((i,j)\) ,有 \(k\) 行 \(l\) 列,那麼當只考慮這個子矩陣時,最優解為
\[ f_{i,j,k,l}=min_{i,j,k,l}+\sum\limits_{x=1}^k\sum\limits_{y=1}^l\textbf A(x+i,y+j). \]
這個狀態轉移方程與上面的等價,但能方便我們用 DP
實現。
至於求子矩陣各元素之和,我們可以利用首碼和的思想,用 \(sum_{i,j}\) 表示左上角坐標為 \((1,1)\) ,右下角坐標為 \((i,j)\) 的子矩陣各元素之和,那麼左上角坐標為 \((i,j)\) 的 \(k\) 行 \(l\) 列的子矩陣各元素之和為
\[ \sum\limits_{x=1}^k\sum\limits_{y=1}^l\textbf A(x+i,y+j)=sum_{i+k-1,j+l-1}-sum_{i+k-1,j-1}-sum_{i-1,j+l-1}+sum_{i-1,j-1}. \]
為了消去 \(-1\),我們設一子矩陣左上角坐標為 \((i+1,j+1)\) ,有 \(k\) 行 \(l\) 列,那麼當只考慮這個子矩陣時,最優解為 \(f_{i,j,k,l}\) 。
核心代碼如下:
// i,j,k,l 的意義已經說明
for(register int i,j,k=1,l,c,xs,ys,min,t;k<=n;++k)
for(l=1;l<=m;++l)
if(k!=1 || l!=1)
for(i=0,xs=n-k;i<=xs;++i)// xs 是為了限制 i,減少運算次數
for(j=0,ys=m-l;j<=ys;++j)//ys 同理
{
// min 表示將該子矩陣切開後的兩個矩陣的最優解的最小值
min=0x7fffffff;
// 橫著切
for(c=1;c<k;++c)
{
t=f[i][j][c][l]+f[i+c][j][k-c][l];
if(t<min)
min=t;
}
// 豎著切
for(c=1;c<l;++c)
{
t=f[i][j][k][c]+f[i][j+c][k][l-c];
if(t<min)
min=t;
}
// 狀態轉移方程
f[i][j][k][l]=min+sum[i+k][j+l]-sum[i+k][j]-sum[i][j+l]+sum[i][j];
}
不用開 O2
就可以過,評測記錄證明一切。