数据范围:1≤n≤16,1≤m≤16,矩阵中每个元素1≤a[i][j]≤1000,1≤r≤n,1≤c≤m
看到数据范围,行与列的范围只有16,首先想到搜索,枚举取舍,时间复杂度达到O(2^(n+m)) ,时间承受不了
对于多维问题,常见的思路是降维
在枚举行之后,对列可以DP。
在选完行以后,每一列上行与行之间的得分就可以计算了,列与列之间的得分即为两列上横向差值的绝对值,这样就把二维压缩成一维了。
2^m -> m^3 时间复杂度骤减
f[i,j]=f[k,j-1]+lc[k,i]+rc[i]
f[i,j]表示以i列结尾,共j列的最小得分
lc[k,i]表示第k列和第i列的横向的差
rc[i]表示第i列上下元素的差
(是我太笨想不到先搜再DP T_T)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define min(a,b) (a<b?a:b)
using namespace std;
int a[20][20],m,n,r,c,ans=2147483647;
int xx[20],rc[20],lc[20][20],f[20][20];
void dp(){
memset(f,0x7f,sizeof f);
memset(rc,0,sizeof rc);
memset(lc,0,sizeof lc);
for(int i=1;i<=m;i++)
for(int j=1;j<i;j++)
for(int k=1;k<=r;k++)
lc[j][i]+=abs(a[xx[k]][i]-a[xx[k]][j]);
for(int i=1;i<=m;i++)
for(int j=1;j<r;j++)
rc[i]+=abs(a[xx[j]][i]-a[xx[j+1]][i]);
for(int i=1;i<=m;i++) f[i][0]=0,f[i][1]=rc[i];
for(int i=1;i<=c;i++)
for(int j=i;j<=m;j++)
for(int k=i-1;k<j;k++)
f[j][i]=min(f[j][i],f[k][i-1]+rc[j]+lc[k][j]);
for(int i=c;i<=m;i++) ans=min(ans,f[i][c]);
return ;
}
void dfs(int x,int las){//已经选了x行
if(x==r) { dp(); return; }
if(r-x>las) return;
for(int i=las;i>=1;i--) xx[r-x]=i,dfs(x+1,i-1);
return ;
}
int main(){
freopen("submatrix.in","r",stdin);
freopen("submatrix.out","w",stdout);
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",&a[i][j]);
dfs(0,n);
printf("%d",ans);
fclose(stdin); fclose(stdout);
return 0;
}