又是dp
暴力枚举会T
考虑先固定一个变量,比如先枚举行
然后预处理每行之间的绝对值,每列之间的绝对值
然后dp进行转移
注意记录选择的行数
转移记得加上新选的列的行之间的绝对值,即w[i],
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 int n, m, r, c; 7 int a[20][20], f[20][20];//f[i][j]表示选择了i列,最后一列为j 8 int w[20], v[20][20], path[20]; 9 int ans=1<<30; 10 void work(){ 11 memset(f,127,sizeof(f)); 12 memset(w,0,sizeof(w)); 13 memset(v,0,sizeof(v)); 14 for(int i=1; i<=m; i++) 15 for(int j=1; j<r; j++) 16 w[i]+=abs(a[path[j]][i]-a[path[j+1]][i]); //记录已选择的行之间第i列的绝对值 17 for(int i=1; i<=m; i++) 18 for(int j=i+1; j<=m; j++) 19 for(int k=1; k<=r; k++) 20 v[i][j]+=abs(a[path[k]][i]-a[path[k]][j]); //预处理每列之间的绝对值差 21 f[0][0]=0; 22 for(int i=1; i<=c; i++) 23 for(int j=i; j<=m; j++) 24 for(int k=0; k<j; k++) 25 f[i][j]=min(f[i][j], f[i-1][k]+w[j]+v[k][j]);//dp 26 for(int i=c; i<=m; i++) ans=min(ans, f[c][i]); //寻找答案 27 28 } 29 void dfs(int now, int pre){ 30 if(now>r){ 31 work(); 32 return ; 33 } 34 for(int i=pre+1; n-i>=r-now; i++){ 35 path[now]=i; 36 dfs(now+1, i); 37 } 38 } 39 int main(){ 40 scanf("%d%d%d%d",&n,&m,&r,&c); 41 for(int i=1; i<=n; ++i) 42 for(int j=1; j<=m; ++j) 43 scanf("%d",&a[i][j]); 44 dfs(1, 0); 45 cout<<ans<<endl; 46 return 0; 47 }