这是一道比较无聊的题目,虽然看上去极其的吓人,但是看完N是小于等于16之后,一切皆有可能
首先,要选出r行c列,我们分开做,暴力枚举出所有的行状态,对于每一种状态分别DP,最后更新答案即可
DP我是这么写的
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示原图第
j
j
j列选了要求矩阵的第
i
i
i列,所得到矩阵的最小值
转移十分的显然
f
[
i
]
[
j
]
=
m
i
n
(
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
(
i
−
1
)
−
>
(
j
−
1
)
]
+
d
e
l
t
a
)
f[i][j]=min(f[i][j],f[i-1][(i-1)->(j-1)]+delta)
f[i][j]=min(f[i][j],f[i−1][(i−1)−>(j−1)]+delta),重点是delta怎么求
我维护了两个数组,
l
i
s
t
list
list表示,当前列内俩俩相邻数的差值和,
l
i
n
e
[
i
]
[
j
]
line[i][j]
line[i][j]表示
i
i
i列和
j
j
j列之间数的差值之和
转移就很清楚了,片段如下
memset(f,127,sizeof(f));
f[0][0]=0;
for(int i=1;i<=c;i++){
for(int j=i;j<=m;j++){
for(int k=i-1;k<j;k++){
f[i][j]=min(f[i][j],f[i-1][k]+list[j]+line[k][j]);
}
}
}
啥,你问我
l
i
s
t
list
list和
l
i
n
e
line
line怎么维护,别问,问就暴力维护
毕竟
C
(
n
,
n
2
)
∗
n
3
2
C(n, \frac n 2 )*\frac {n^3} 2
C(n,2n)∗2n3最大才2500w左右
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 26;
int n,m,r,c,g[maxn][maxn],f[maxn][maxn];
int cho[maxn],ans=523454543,line[maxn][maxn],list[maxn];
void Checking(){
memset(list,0,sizeof(list));
memset(line,0,sizeof(line));
for(int i=1;i<=m;i++){
for(int j=1;j<=r;j++){
int l=cho[j];
list[i]+=abs(g[l][i]-g[cho[j-1]][i]);
for(int k=i+1;k<=m;k++){
line[i][k]+=abs(g[l][i]-g[l][k]);
}
}
list[i]-=g[cho[1]][i];
}
memset(f,127,sizeof(f));
f[0][0]=0;
for(int i=1;i<=c;i++){
for(int j=i;j<=m;j++){
for(int k=i-1;k<j;k++){
f[i][j]=min(f[i][j],f[i-1][k]+list[j]+line[k][j]);
}
}
}
for(int i=c;i<=m;i++)ans=min(ans,f[c][i]);
}
void dfs(int x,int k){
if(k==r){
Checking();
return ;
}
else{
for(int i=x;i<=n;i++){
cho[k+1]=i;
dfs(i+1,k+1);
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&r,&c);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&g[i][j]);
dfs(1,0);
cout<<ans;
}
/*
3 3 3 3
1 2 3
6 5 4
2 3 4
*/