dfs+dp
我们要在一个大矩阵中选出一个小矩阵使得小矩阵的分值最小
所以我们先用dfs枚举选哪些行,再在dfs选的行的基础上对列进行dp,最后更新答案即可。
具体地,我们先通过dfs枚举r行,然后计算两个数组:sum[i][j]表示在当前dfs确定的行的状态下,第i列和第j列相邻时产生的分值。s[i]表示在当前dfs确定的行的状态下,第i列选出r行后的分值。
之后,我们对列进行一次dp,定义f[i][j]表示在当前dfs确定的行的状态下,前i列中选了j列(其中第i列选)的分值,那么状态转移方程很简单。
所以我们就解决了这个问题。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 using namespace std; 7 typedef long long ll; 8 int n,m,r,c; 9 inline int read() { 10 int ret=0; 11 int op=1; 12 char c=getchar(); 13 while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();} 14 while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar(); 15 return ret*op; 16 } 17 int a[20][20],ans=999999999; 18 int f[20][20],now[20],sum[20][20],s[20]; 19 void calc() { 20 memset(s,0,sizeof(s)); 21 memset(sum,0,sizeof(sum)); 22 for(int i=1;i<=m;i++) 23 for(int j=2;j<=r;j++) 24 s[i]+=abs(a[now[j]][i]-a[now[j-1]][i]); 25 for(int i=1;i<=m;i++) 26 for(int j=i+1;j<=m;j++) 27 for(int k=1;k<=r;k++) 28 sum[i][j]+=abs(a[now[k]][i]-a[now[k]][j]); 29 } 30 int dp() { 31 int ret=999999999; 32 memset(f,0x3f,sizeof(f)); 33 for(int i=1;i<=m;i++) { 34 f[i][1]=s[i]; 35 for(int j=2;j<=c;j++) 36 for(int k=1;k<i;k++) 37 f[i][j]=min(f[i][j],f[k][j-1]+s[i]+sum[k][i]); 38 ret=min(ret,f[i][c]); 39 } 40 return ret; 41 } 42 void dfs(int u) { 43 if(u==r+1) { 44 calc(); 45 ans=min(ans,dp()); 46 return ; 47 } 48 for(int i=now[u-1]+1;i<=n;i++) { 49 now[u]=i; 50 dfs(u+1); 51 now[u]=0; 52 } 53 } 54 int main() { 55 n=read(); m=read(); r=read(); c=read(); 56 for(int i=1;i<=n;i++) 57 for(int j=1;j<=m;j++) 58 a[i][j]=read(); 59 dfs(1); 60 printf("%d ",ans); 61 return 0; 62 }