P2258 子矩阵——搜索+dp
二进制枚举套二进制枚举能过多一半的点;
我们只需要优化一下第二个二进制枚举的部分;
首先我们先枚举选哪几行,再预处理我们需要的差值,上下,左右;
sum_shang,sum_heng
然后DP查找最小值
dp[i][j]表示前i列已经选了j列;
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=20; int n,m,r,c; int a[maxn][maxn]; int id[maxn]; int b[maxn]; int sum_s[maxn]; int sum_h[maxn][maxn]; void pre_pare() { memset(sum_s,0,sizeof(sum_s)); int num=0; for(int i=1;i<=n;i++) if(b[i]) id[++num]=i; for(int i=1;i<r;i++) { for(int j=1;j<=m;j++) { sum_s[j]+=abs(a[id[i]][j]-a[id[i+1]][j]); } } for(int i=1;i<=m;i++) { for(int j=i+1;j<=m;j++) { sum_h[i][j]=0; for(int k=1;k<=r;k++) { sum_h[i][j]+=abs(a[id[k]][i]-a[id[k]][j]); } } } } int ans=2147483647,res=2147483647; int dp[maxn][maxn]; int query() { memset(dp,0x3f,sizeof(dp)); res=2147483647; for(int i=1;i<=m;i++) { dp[i][1]=sum_s[i]; for(int j=2;j<=c;j++) { for(int k=1;k<i;k++) { dp[i][j]=min(dp[i][j],dp[k][j-1]+sum_s[i]+sum_h[k][i]); } } res=min(res,dp[i][c]); } return res; } void dfs(int x,int sum) { if(sum>r) return ; if(x==n+1) { if(sum!=r) return ; pre_pare(); ans=min(ans,query()); return ; } b[x]=0; dfs(x+1,sum); b[x]=1; dfs(x+1,sum+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",&a[i][j]); } } dfs(1,0); printf("%d",ans); return 0; }