NOIP2014普及组 子矩阵

链接:https://ac.nowcoder.com/acm/problem/16503
来源:牛客网

题目描述

给出如下定义:
1.子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵。

例如,下面左图中选取第 2 、 4 行和第 2 、 4 、 5 列交叉位置的元素得到一个 2 x 3 的子矩阵如右图所示。

 

9 3 3 3 9
9 4 8 7 4
1 7 4 6 6
6 8 5 6 9
7 4 5 6 1

 


的其中一个 2 x 3 的子矩阵是

 

4 7 4
8 6 9

 

2.相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的。

3.矩阵的分值:矩阵中每一对相邻元素之差的绝对值之和。

本题任务:给定一个 n 行 m 列的正整数矩阵,请你从这个矩阵中选出一个 r 行 c 列的子矩阵,使得这个子矩阵的分值最小,并输出这个分值。

输入描述:

输入第一行包含用空格隔开的四个整数 n,m,r,c ,意义如问题描述中所述,每两个整数之间用一个空格隔开。

接下来的 n 行,每行包含 m 个用空格隔开的整数,用来表示问题描述中那个 n 行 m 列的矩阵。

输出描述:

一个整数,表示满足题目描述的子矩阵的最小分值。
示例1

输入

复制
5 5 2 3
9 3 3 3 9
9 4 8 7 4
1 7 4 6 6
6 8 5 6 9
7 4 5 6 1

输出

复制
6

说明

该矩阵中分值最小的 2 行 3 列的子矩阵由原矩阵的第 4 行、第 5 行与第 1 列、第 3 列、第 4 列交叉位置的元素组成,为
6 5 6
7 5 6
其分值为:|6−5| + |5−6| + |7−5| + |5−6| + |6−7| + |5−5| + |6−6| =6。


解题思路:枚举选出行的情况,然后对列进行DP就可以了。dp【i】【j】为选了j列,并且右边第一列为第i列的值。那么dp【i】【j】=min(dp【k】【j】+加入第i列对行之间的绝对值的影响+加入i列对列之间的绝对值的影响)。
至于这两个“影响”怎么求看代码中的“init”吧。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=20;
typedef long long ll;
int a[maxn][maxn];
int n,m,r,c;
int vis[maxn];
int dp[maxn][maxn];
vector<int> vec;
int hang[maxn],lie[maxn][maxn];
int ans;
void init(){
    memset(hang,0,sizeof(hang));
    memset(lie,0,sizeof(lie));
    for(int i=1;i<=n;i++){
        if(vis[i]==1)
        vec.push_back(i);
    }
    int len=vec.size();
    for(int i=1;i<=m;i++){
        for(int j=0;j<len-1;j++){
            hang[i]+=abs(a[vec[j]][i]-a[vec[j+1]][i]);
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=i+1;j<=m;j++){
            for(int k=0;k<len;k++){
                lie[i][j]+=abs(a[vec[k]][i]-a[vec[k]][j]);
            }
            lie[j][i]=lie[i][j];
        }
        
    }
    vec.clear();
}
void DP(){
    for(int i=1;i<=m;i++){
        dp[i][c]=inf;
        for(int j=1;j<=i;j++){
            if(j==1)dp[i][j]=hang[i];
            else if(i==j){
                dp[i][j]=dp[i-1][j-1]+hang[i]+lie[i-1][i];
            }
            else{
                dp[i][j]=inf;
                for(int k=j-1;k<i;k++){
                    dp[i][j]=min(dp[i][j],dp[k][j-1]+hang[i]+lie[k][i]);
                }
            }
            
        }
            ans=min(ans,dp[i][c]);
    }
}
void solve(int be,int cnt){
    if(cnt==r){
        init();
        DP();
        return ;
    }
    if(be>n){
        return ;
    }
    
        vis[be]=1;
        solve(be+1,cnt+1);
        vis[be]=0;
        solve(be+1,cnt);
    
    return ;
}
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]);
        }
    }
    ans=inf;
    solve(1,0);
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2019-08-07 00:16  风雨兼程-zhi  阅读(596)  评论(0编辑  收藏  举报