(题目描述略)
此题虽数据范围较小,但单纯的搜索算法仍不免超时。提高时间效率的办法仍然是以空间换时间,枚举行,预处理出子矩阵相邻元素的差值,再用动态转移求出选定行的分值最小的子矩阵。几乎多数基于二维矩阵的极值问题都可以用这种方法求解。
若选定的行数记为row[i] (i=[0,r)),选定行数第i列和第j列元素之差记为tempx[i][j] (i,j=[0,m)且i < j),第i列选定行数相邻行元素之差的和记为tempy[i] (i=[0,m)),设f[i][j]为前i+1列取j+1列且必取第i列的最小分值,边界f[0][0]=tempy[0],则动态转移方程如下:
f[0][0]=tempy[0]
f[i][i]=f[i-1][i-1]+tempx[i-1][i]+tempy[i] (i=(0,m))
f[i][j]=max(f[k][j-1]+tempx[k][i]+tempy[i]) (k=[j-1,i))
代码如下:
#include"stdio.h"
int f[20][20],matrix[20][20],n,r,row[20],tempx[20][20],tempy[20];
int abs(int x)
{
return x<0?-x:x;
}
bool next()//求下一个行组合,不存在则返回true
{
int t;
for(t=r-1;t>=0;t--)
if(row[t]<n-r+t)
break;
if(t<0)
return true;
for(row[t++]++;t<r;t++)
row[t]=row[t-1]+1;
return false;
}
int main()
{
freopen("submatrix.in","r",stdin);
freopen("submatrix.out","w",stdout);
int ans=-1,c,m;
scanf("%d %d %d %d",&n,&m,&r,&c);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&matrix[i][j]);
for(int i=0;i<r;i++)
row[i]=i;//初始化行
while(true)
{
for(int i=0;i<m;i++)
{
tempy[i]=0;
for(int j=i+1;j<m;j++)
{
tempx[i][j]=0;
for(int k=0;k<r;k++)
tempx[i][j]+=abs(matrix[row[k]][i]-matrix[row[k]][j]);
}
for(int j=1;j<r;j++)
tempy[i]+=abs(matrix[row[j-1]][i]-matrix[row[j]][i]);
}//预处理,求tempx和tempy
f[0][0]=tempy[0];
for(int i=1;i<m;i++)
{
f[i][i]=f[i-1][i-1]+tempx[i-1][i]+tempy[i];
for(int j=i-1;j>0;j--)
{
f[i][j]=f[j-1][j-1]+tempx[j-1][i]+tempy[i];
for(int k=j;k<i;k++)
if(f[i][j]>f[k][j-1]+tempx[k][i]+tempy[i])
f[i][j]=f[k][j-1]+tempx[k][i]+tempy[i];
}
f[i][0]=tempy[i];
if(i>=c-1&&(ans==-1||ans>f[i][c-1]))
ans=f[i][c-1];
}
if(next())
break;
}
printf("%d",ans);
return 0;
}