zoukankan      html  css  js  c++  java
  • P2258 子矩阵

    题目描述

    给出如下定义:

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

    例如,下面左图中选取第 222 、 444 行和第 222 、 444 、 555 列交叉位置的元素得到一个 2×32 imes 32×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×32 imes 32×3 的子矩阵是

    4 7 4

    8 6 9

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

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

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

    (本题目为2014NOIP普及## 题目描述

    给出如下定义:

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

    例如,下面左图中选取第 222 、 444 行和第 222 、 444 、 555 列交叉位置的元素得到一个 2×32 imes 32×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×32 imes 32×3 的子矩阵是

    4 7 4

    8 6 9

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

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

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

    (本题目为2014NOIP普及T4)

    输入输出格式

    输入格式:

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

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

    输出格式:

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

    输入输出样例

    输入样例#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

    输出样例#1: 复制

    6

    输入样例#2: 复制

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

    输出样例#2: 复制

    16

    说明

    【输入输出样例1说明】

    该矩阵中分值最小的 222 行 333 列的子矩阵由原矩阵的第 444 行、第 555 行与第 111 列、第 333 列、第 444 列交叉位置的元素组成,为

    6 5 6

    7 5 6

    ,其分值为:

    |6−5| + |5−6| + |7−5| + |5−6| + |6−7| + |5−5| + |6−6| =6。

    【输入输出样例2说明】

    该矩阵中分值最小的3行3列的子矩阵由原矩阵的第 444 行、第 555 行、第 666 行与第 222 列、第 666 列、第 777 列交叉位置的元素组成,选取的分值最小的子矩阵为

    9 7 8
    9 8 8
    5 8 10

    【数据说明】

    对于 50%50%50% 的数据, 1≤n≤12,1≤m≤121 ≤ n ≤ 12,1 ≤ m ≤ 121≤n≤12,1≤m≤12 ,矩阵中的每个元素 1≤aij≤201 ≤ a_{ij} ≤ 201≤aij​≤20 ;

    对于 100%100%100% 的数据, 1≤n≤16,1≤m≤161 ≤ n ≤ 16,1 ≤ m ≤ 161≤n≤16,1≤m≤16 ,矩阵中的每个元素 1≤aij≤1,000,1≤r≤n,1≤c≤m1 ≤ a_{ij} ≤ 1,000,1 ≤ r ≤ n,1 ≤ c ≤ m1≤aij​≤1,000,1≤r≤n,1≤c≤m 。T4)

    输入输出格式

    输入格式:

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

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

    输出格式:

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

    输入输出样例

    输入样例#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

    输出样例#1: 复制

    6

    输入样例#2: 复制

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

    输出样例#2: 复制

    16

    说明

    【输入输出样例1说明】

    该矩阵中分值最小的 222 行 333 列的子矩阵由原矩阵的第 444 行、第 555 行与第 111 列、第 333 列、第 444 列交叉位置的元素组成,为

    6 5 6

    7 5 6

    ,其分值为:

    |6−5| + |5−6| + |7−5| + |5−6| + |6−7| + |5−5| + |6−6| =6。

    【输入输出样例2说明】

    该矩阵中分值最小的3行3列的子矩阵由原矩阵的第 444 行、第 555 行、第 666 行与第 222 列、第 666 列、第 777 列交叉位置的元素组成,选取的分值最小的子矩阵为

    9 7 8
    9 8 8
    5 8 10

    【数据说明】

    对于 (50\%)的数据, (1 ≤ n ≤ 12,1 ≤ m ≤ 12),矩阵中的每个元素 (1 ≤ a_{ij} ≤ 20)

    对于 (100\%) 的数据, (1≤n≤16,1≤m≤16) ,矩阵中的每个元素 (1 ≤ a_{ij} ≤ 1,000,1 ≤ r ≤ n,1 ≤ c ≤ m)


    先爆搜出(r)行,再在选出的行中dp选出(c)列即可

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int i,m,n,j,k,a[20][20],b[20][20],r,c,d[20],w[20],f[20][20],ans=0x3f3f3f3f;
    
    void dp()
    {
    	memset(w,0,sizeof(w));
    	memset(b,0,sizeof(b));
    	memset(f,0x3f,sizeof(f));
    	for(int i=1;i<=m;i++)
    		for(int j=2;j<=r;j++) 
    			w[i]+=abs(a[d[j]][i]-a[d[j-1]][i]);
    	for(int i=1;i<m;i++)
    		for(int j=i+1;j<=m;j++) 
    			for(int l=1;l<=r;l++)
    				b[i][j]+=abs(a[d[l]][i]-a[d[l]][j]);
    	f[0][0]=0;
    	for(int i=1;i<=m;i++)
    		for(int j=1;j<=min(i,c);j++)
    			for(int l=j-1;l<i;l++)
    				f[i][j]=min(f[i][j],f[l][j-1]+b[l][i]+w[i]);
    	for(int i=c;i<=m;i++) ans=min(ans,f[i][c]);
    }
    
    void dfs(int k,int now)
    {
    	if(k==r) {dp(); return;}
    	for(int i=now;i<=n-r+k+1;i++) d[k+1]=i,dfs(k+1,i+1);
    }
    
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&r,&c);
    	for(i=1;i<=n;i++)
    		for(j=1;j<=m;j++) scanf("%d",&a[i][j]);
    	dfs(0,1);
    	printf("%d",ans);
    }
    
  • 相关阅读:
    Ubuntu vi命令
    Aria2 使用
    axel 参数 文件下载地址
    序列化模块、加密模块
    项目开发规范、time模块、日志
    递归函数(了解)
    模块
    函数进阶四(装饰器、内置函数)
    函数进阶三(生成器、生成器表达式、匿名函数)
    函数进阶二(函数名运用,闭包,迭代器)
  • 原文地址:https://www.cnblogs.com/ZUTTER/p/9509605.html
Copyright © 2011-2022 走看看