子矩阵
搜索枚举选了哪几行,将DP降为一个一维的问题,
先预处理出w[i]表示该列上下元素差的绝对值之和
v[i][j]为第i列和第j列对应元素之差的绝对值之和
f[i][j]表示前j列中选i列,且最后一列为j的最小消耗
f[i][j]=min(f[i][j],f[i-1][j-k]+v[j-k][j]+w[j]);
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; #define reset(a) memset(a,0,sizeof(a)) #define _reset(a) memset(a,127,sizeof(a)) #define N 17 int n,m,r,c,a[N][N],ans=0x3f3f3f3f; int f[N][N],w[N],v[N][N],path[N]; inline int read(){ int x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while('0'<=c&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } return x; } void dp(){ reset(w); reset(v); _reset(f); for(int i=1;i<=m;i++) for(int j=2;j<=r;j++) w[i]+=abs(a[path[j]][i]-a[path[j-1]][i]); for(int i=1;i<m;i++) for(int j=i+1;j<=m;j++) for(int k=1;k<=r;k++) v[i][j]+=abs(a[path[k]][i]-a[path[k]][j]); f[0][0]=0; for(int i=1;i<=c;i++) for(int j=i;j<=m;j++) for(int k=0;k<j;k++) f[i][j]=min(f[i][j],f[i-1][k]+w[j]+v[k][j]); for(int i=c;i<=m;i++) ans=min(ans,f[c][i]); } void dfs(int t,int minn){ if(t==r+1){ dp(); return; } for(int i=minn;i<=n;i++){ path[t]=i; dfs(t+1,i+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++) a[i][j]=read(); dfs(1,1); printf("%d ",ans); return 0; }