题意:给你一个n×m的矩阵(n,m<=16),要你从中选r行c列,行和列的相交部分为该矩阵的子矩阵,一个子矩阵的分值为相邻元素的差的绝对值之和,你需要最小化子矩阵的分值
题解:
暴搜+dp
暴搜出列的选择方案,然后用dp决策行的选择方案
dp[i][j]表示选择了第i行,总共已经选了j行的最小分值,w[i]为i行的分值,v[i][k]为i行与k行相接后获得的分值
dp[i][j]=min(dp[i][j],dp[k][j-1]+v[i][k]+w[i])
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define N 20
using namespace std;
int n,m,r,c,ans=1<<30;
int a[N],g[N][N],dp[N][N],w[N],v[N][N];
bool bj[N];
int gi() {
int x=0,o=1; char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') o=-1,ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return o*x;
}
void cal() {
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
memset(dp,0,sizeof(dp));
int ret=1<<30;
for(int i=1; i<=n; i++)
for(int j=1; j<c; j++)
w[i]+=abs(g[i][a[j]]-g[i][a[j+1]]);
for(int i=0; i<=n; i++)
for(int k=i+1; k<=n; k++)
for(int j=1; j<=c; j++)
v[i][k]+=abs(g[i][a[j]]-g[k][a[j]]);
memset(dp,63,sizeof(dp));
for(int i=1; i<=n; i++) dp[i][0]=0,dp[i][1]=w[i];
for(int i=1; i<=n; i++)
for(int k=i+1; k<=n; k++)
for(int j=1; j<=i+1; j++)
dp[k][j]=min(dp[k][j],dp[i][j-1]+w[k]+v[i][k]);
for(int i=r; i<=n; i++)
ret=min(ret,dp[i][r]);
ans=min(ans,ret);
}
void dfs(int dep) {
if(dep==c+1) {
cal();
return;
}
for(int i=a[dep-1]+1; i<=m; i++) {
if(bj[i]) continue;
bj[i]=1,a[dep]=i;
dfs(dep+1);
bj[i]=0,a[dep]=0;
}
}
int main() {
n=gi(),m=gi(),r=gi(),c=gi();
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
g[i][j]=gi();
dfs(1);
printf("%d", ans);
return 0;
}