题解 P2331 【[SCOI2005]最大子矩阵】
interestingLSY
2018-10-23 14:57:32
## 此篇题解为下面几篇的小优化(如何把代码写短)请先阅读透其他题解之后再来qwq
咳咳,思路楼下(楼上?)的dalao已经说得非常好了。
只不过。。。您们的代码好长啊!!!
### 那么在这里,我将介绍,怎么写出一份**短小精悍**的代码
话不多说,先上菜(下面有讲解
```cpp
#include <bits/stdc++.h>
using namespace std;
#define rg register
#define INF 0x3f3f3f3f
#define max(a,b) ((a)>(b)?(a):(b))
#define Mymax(a,b) ((a)=max(a,b))
#define Msn(a,x) memset(a,x,sizeof a)
#define For(i,j) for( rg int (i) = 1 ; (i) <= (j) ; ++(i) )
#define For0(i,j) for( rg int (i) = 0 ; (i) < (j) ; ++(i) )
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
#define MAXN 110
#define MAXM 4
#define MAXK 12
const int Tran[6][6] = {
{0,0,0,0,0},
{1,0,1,1,0},
{1,1,0,1,0},
{1,1,1,0,2},
{2,1,1,2,0}
};
const int sel[6][3] = {
{0,0},
{1,0},
{0,1},
{1,1},
{1,1}
};
int n,m,k;
int a[MAXN][MAXM];
int mem[MAXN][MAXK][6];
#define NOW mem[pos][used][stat]
int Dfs( int pos , int used , int stat ){
if( used + Tran[stat][0] > k ) return -INF;
if( pos == n+1 ) return 0;
if( NOW != -1 ) return NOW; // 记忆化
int ans = -INF;
For0(ns,5){
int tans = Dfs(pos+1,used+Tran[stat][ns],ns) + sel[ns][0]*a[pos][1] + sel[ns][1]*a[pos][2];
Mymax(ans,tans);
}
return NOW = ans;
}
int main(){
Msn(mem,-1);
cin >> n >> m >> k;
For(i,n)
For(j,m)
cin >> a[i][j];
int ans = Dfs(1,0,0);
cout << ans << endl;
return 0;
}
```
哇好短啊
参照楼下的思路,我们的stat表示上一列的状态
- 0 空出这一行
- 1 选择左边空出右边
- 2 选择右边空出左边
- 3 选择这一行两个(两个一块作为一个矩阵的一部分)
- 4 选择这一行两个(不作为一个矩阵,而是左边一列单独一个矩阵,右边单独一个矩阵)
(这是楼下的思路,**同时请注意状态3,4的顺序有所不同**)
那么需要分类讨论吗?不用!
其实,核心就是两个二维数组(矩阵?)—— $Tran$ 和 $Sel$
- $Tran_{i,j}$ 记录从状态 $i$ 到状态 $j$ ,需要 “截止” 当前多少矩阵(我在dp时,$used$ 中**不包含**当前还没截止的矩阵
- $Sel_{i,j}$ 记录状态 $i$ 是否选第 $j+1$ 列(数组下标从0开始)
所以,转移的时候直接从 $Dfs(pos,used,stat)$ 转移到 $Dfs(pos+1,used+Tran_{stat,newstat},newstat)$ 即可!
但是我们怎么判断当前状态是否超出了 “只能选k个” 的限制?
直接在递归调用时判断,当然会很麻烦。
那怎么办?直接给下一波dfs判断就行!具体的,判断 $Tran_{stat,0}+used$ 是不是小于等于 $k$!