二进制枚举套二进制枚举能过多一半的点;
我们只需要优化一下第二个二进制枚举的部分;
首先我们先枚举选哪几行,再预处理我们需要的差值,上下,左右;
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;
}
