zoukankan      html  css  js  c++  java
  • 洛谷2258 子矩阵

    题目描述

    给出如下定义:

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

      例如,下面左图中选取第2、4行和第2、4、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

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

        4 7 4

        8 6 9

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

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

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

    输入输出格式

    输入格式:

    第一行包含用空格隔开的四个整数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

    输出样例#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说明】

    该矩阵中分值最小的23列的子矩阵由原矩阵的第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

    【输入输出样例2说明】

    该矩阵中分值最小的3行3列的子矩阵由原矩阵的第4行、第5行、第6行与第2列、第6列、第7列交叉位置的元素组成,选取的分值最小的子矩阵为

      9 7 8
      9 8 8
      5 8 10

    【数据说明】

    对于50%的数据,1 ≤ n ≤ 12,1 ≤ m ≤ 12,矩阵中的每个元素1 ≤ aij ≤ 20

    对于100%的数据,1 ≤ n ≤ 16,1 ≤ m ≤ 16,矩阵中的每个元素1 ≤ aij ≤ 1,000,1 ≤ r ≤ n,1 ≤ c ≤ m。

    题解

    解析

    本题目为2014NOIP普及T4,应该是一道很有名的题目,洛谷上的难度是提高+/省选-。其实,如果想通了,这题真的不是很难。

    本题的题意就是在一个n行m列的正整数矩阵中,选出一个r行c列的矩阵,使得该矩阵中每一对相邻元素之差的绝对值之和最小。

    首先,选行的话可以用DFS排列出所有r行的情况(可以用回溯)

    接着,可以进行预处理,对所选的r行而言,处理出它所有元素的“分值”;

         对于上下两行元素之间的差,要一维数组(因为DFS选行时,就相当于有一个数组了);

         对于相邻两列的处理,必选选用二维数组(需要存储行的个数和列的个数);

         或许这样讲不太清楚,那么可以这么理解:

         1.设v[i]表示所选出的相邻两行第i个元素的差的绝对值

       2.设w[i][j]表示所选出的第i-1行和第i行的第j列上所有元素的差的绝对之和

    然后,就可以动规了,本题就转化为在m列中选出c列,满足最小即可(有点像背包),但是要注意边界问题:

       1.第一层循环毫无疑义是i从1~m枚举,注意第二层循环就是j从1~min(i,c),因为只要选c列

       2.当j等于1时,f[i][j]=v[i],这应该不需要解释

       3.当j等于i时,即前i列都取,那么f[i-1][j-1]+v[i]+w[i][j-1];

    把算法再整理一遍

    1.通过DFS求出在n行中取出r行的所有情况

    2.预处理出行差和列差

    3.通过动规(类似背包问题)计算出最优值

    代码

      1 #include <set>
      2 #include <map>
      3 #include <queue>
      4 #include <stack>
      5 #include <ctime>
      6 #include <cmath>
      7 #include <cstdio>
      8 #include <cstring>
      9 #include <cstdlib>
     10 #include <iostream>
     11 #include <algorithm>
     12 
     13 using namespace std;
     14 
     15 const int N=20;
     16 const int INF=2147483647;
     17 
     18 int ans=INF;
     19 
     20 int n,m,r,c;
     21 int a[N][N];
     22 
     23 int tmp=1;
     24 int h[N]={0};//hang
     25 int w[N][N]={0},v[N]={0};
     26 int f[N][N];
     27 
     28 void before_work()
     29 {
     30     memset (w,0,sizeof (w));
     31     memset (v,0,sizeof (v));
     32     for (int i=1;i<=m;i++)
     33         for (int j=1;j<r;j++)
     34             v[i]=abs(a[h[j]][i]-a[h[j+1]][i]);
     35     for (int i=2;i<=m;i++)
     36         for (int j=1;j<i;j++)
     37             for (int k=1;k<=r;k++)
     38                 w[i][j]+=abs(a[h[k]][i]-a[h[k]][j]);
     39     return;
     40 }
     41 
     42 void DP()
     43 {
     44     for (int i=1;i<=m;i++)
     45     {
     46         for (int j=1;j<=min(i,c);j++)
     47         {
     48             if (j==1)//边界1
     49                 f[i][j]=v[i];
     50             else if (i==j)//边界2
     51                 f[i][j]=f[i-1][j-1]+v[i]+w[i][j-1];
     52             else
     53             {
     54                 f[i][j]=INF;
     55                 for (int k=j-1;k<i;k++)
     56                     f[i][j]=min(f[i][j],f[k][j-1]+v[i]+w[i][k]);
     57             }
     58             if (j==c)ans=min(ans,f[i][c]);//求答案
     59         }
     60     }
     61 }
     62 
     63 void DFS(int cnt)
     64 {
     65     if (cnt>n)
     66     {
     67         before_work();//预处理
     68         DP();//动规
     69         return;
     70     }
     71     if (r-tmp+1==n-cnt+1)
     72     {//一个小优化。如果cnt和cnt以后的元素必须全部取完,才能选出r个,那么cnt必须取,否则就取不到r个元素。
     73         h[tmp++]=cnt;
     74         DFS(cnt+1);
     75         h[tmp--]=0;
     76         return;
     77     }
     78     DFS(cnt+1);//这一行不取
     79     if (tmp<=r)
     80     {//这一行取
     81         h[tmp++]=cnt;
     82         DFS(cnt+1);
     83         h[tmp--]=0;
     84     }
     85 }
     86 
     87 void work()
     88 {
     89     scanf ("%d%d%d%d",&n,&m,&r,&c);
     90     for (int i=1;i<=n;i++)
     91         for (int j=1;j<=m;j++)
     92             scanf ("%d",&a[i][j]);
     93     DFS(1);
     94     printf ("%d
    ",ans);
     95     return;
     96         
     97 }
     98 
     99 int main()
    100 {
    101     work();
    102     return 0;
    103 }

    出处:https://www.cnblogs.com/yujustin/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    温馨提示:尽量使用版本较高的浏览器,并打开极速模式。
  • 相关阅读:
    CentOS7基础优化与常用配置
    MHA+binlogserver+VIP+sendreport 高可用架构
    GTID主从复制
    主从复制故障处理
    主从复制
    mysql 配置文件
    通用二进制安装mysql-5.7.28(无坑)
    css动画 文字闪烁效果
    cmd命令提示符大全(干货)
    JS实现手机摇一摇功能
  • 原文地址:https://www.cnblogs.com/yujustin/p/9775455.html
Copyright © 2011-2022 走看看