视频讲解戳这里 (bj聚聚讲的可好了)
解题思路:这题一看和最大子矩阵很类似。但是要比那题更加的复杂,考虑时有诸多细节。思路就是先把行确定下来,这里用一个搜索,然后再去考虑列,考虑列的时候,我们需要预处理我们所选行 的竖直的分值(即题目定义的相邻元素差的绝对值)和横置的分值。需要两个数组,横置 line[i][j]代表 第i列和第j列横向的之差的绝对值之和;竖直col[i],代表第i列竖直之差的绝对值之和。预处理结束之后,就是一个dp了。 dp[i][j] 表示共选择了j列,最后一列是i列的最小值
递推式就是: dp[i][j]=min(dp[i][j],dp[k][j-1]+col[i]+line[k][i]); 丑陋的代码如下
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=20;
int n,m,s,c,ans=inf;
int a[maxn][maxn];
int num=0,choose[maxn];
int dp[maxn][maxn]; //dp[i][j] 选择了j列,最后一列是i列的最小值
int line[maxn][maxn],col[maxn];
// 行 列
void init(){
//预处理竖直的列
for(int j=1;j<=m;++j){
col[j]=0;
for(int i=2;i<=s;++i){
col[j]+=abs(a[choose[i]][j]-a[choose[i-1]][j]);
}
}
//预处理行
for(int i=2;i<=m;++i){
for(int j=1;j<i;++j){
line[j][i]=0;
for(int k=1;k<=s;++k){
line[j][i]+=abs(a[choose[k]][i]-a[choose[k]][j]);
}
}
}
memset(dp,0x3f,sizeof(dp));
}
void solve(){ //再行确定的情况下 考虑列
init();
for(int i=1;i<=m;++i){
dp[i][1]=col[i];
if(c==1){
ans=min(ans,dp[i][1]);
}
}
for(int j=2;j<=c;++j){
for(int i=j;i<=m && m-i+j>=c;++i){
for(int k=j-1;k<i;++k){
dp[i][j]=min(dp[i][j],dp[k][j-1]+col[i]+line[k][i]);
}
if(j==c)
ans=min(ans,dp[i][j]);
}
}
}
void dfs(int step){ //搜索所有行的组合
if(num==s){
solve();
}
else{
for(int i=step;num+n-i+1>=s;++i){
num++;
choose[num]=i;
dfs(i+1);
num--;
}
}
}
int main(){
std::ios::sync_with_stdio(0);
cin>>n>>m>>s>>c;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cin>>a[i][j];
}
}
dfs(1);
cout<<ans<<endl;
return 0;
}