【洛谷P2258】子矩阵

子矩阵

题目链接

搜索枚举选了哪几行,将DP降为一个一维的问题,

先预处理出w[i]表示该列上下元素差的绝对值之和

v[i][j]为第i列和第j列对应元素之差的绝对值之和

f[i][j]表示前j列中选i列,且最后一列为j的最小消耗

f[i][j]=min(f[i][j],f[i-1][j-k]+v[j-k][j]+w[j]);

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define reset(a) memset(a,0,sizeof(a))
#define _reset(a) memset(a,127,sizeof(a))
#define N 17
int n,m,r,c,a[N][N],ans=0x3f3f3f3f;
int f[N][N],w[N],v[N][N],path[N];
inline int read(){
    int x=0; char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while('0'<=c&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
    return x;
}
void dp(){
    reset(w); reset(v);
    _reset(f);
    for(int i=1;i<=m;i++)
     for(int j=2;j<=r;j++)
      w[i]+=abs(a[path[j]][i]-a[path[j-1]][i]);
    for(int i=1;i<m;i++)
     for(int j=i+1;j<=m;j++)
      for(int k=1;k<=r;k++)
       v[i][j]+=abs(a[path[k]][i]-a[path[k]][j]);
    f[0][0]=0;
    for(int i=1;i<=c;i++)
     for(int j=i;j<=m;j++)
      for(int k=0;k<j;k++)
       f[i][j]=min(f[i][j],f[i-1][k]+w[j]+v[k][j]);
    for(int i=c;i<=m;i++)
     ans=min(ans,f[c][i]);
}
void dfs(int t,int minn){
    if(t==r+1){
        dp();
        return;
    }
    for(int i=minn;i<=n;i++){
        path[t]=i;
        dfs(t+1,i+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++)
      a[i][j]=read();
    dfs(1,1);
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2018-08-03 19:23  yjk  阅读(148)  评论(0编辑  收藏  举报