洛谷 P2258 子矩阵 题解

xiaoxiao2021-02-28  164

今天看了看枚举与搜索的题,意外中看到了一道“看似简单”的普及组题,然而事实是我想了好久,然后终于搞出来了。 题目:

题目描述

给出如下定义:

子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵。 例如,下面左图中选取第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

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

矩阵的分值:矩阵中每一对相邻元素之差的绝对值之和。 本题任务:给定一个n行m列的正整数矩阵,请你从这个矩阵中选出一个r行c列的子矩阵,使得这个子矩阵的分值最小,并输出这个分值。

(本题目为2014NOIP普及T4)

输入输出格式

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

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

输出格式: 输出共1行,包含1个整数,表示满足题目描述的子矩阵的最小分值。

输入输出样例

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

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

6 5 6

7 5 6

,其分值为

|65|+|56|+|75|+|56|+|67|+|55|+|66|=6

【输入输出样例2说明】

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

9 7 8 9 8 8 5 8 10

【数据说明】

对于50%的数据, 1n121m12 ,矩阵中的每个元素 1a[i][j]20

对于100%的数据, 1n161m16 ,矩阵中的每个元素 1a[i][j]1,000

1rn1cm

分析: 最暴力的算法是枚举选择哪些行、列。复杂度为 O(C(n,r)×C(m,c)) 。不过显然不能承受。(C为组合数) 注意到虽然 O(C(n,r)×C(m,c)) 不能承受,但 O(C(n,r)) O(C(m,c)) 是可以接受的。 不妨考虑枚举其中一个(假设枚举行)。 枚举完行后,由于行已确定,因此可以把所有行捆绑,视为一个整体。 处理处列与列之间的价值,然后可以用动态规划解决这个问题。 设 dp[i][k] 表示前i列选了k列,并且第i列强制被选。那么转移方程为: dp[i][k]=dp[j][k1]+cost[j][i]+val[i] ,其中 j<icost[j][i] 表示第i列与第j列相邻的花费, val[i] 表示第i列内的花费。 答案即为 mindp[i][c] 。 代码:

#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef double DB; const int inf = 1e9 + 7; int n, m, r, c; int ans = inf; int a[17][17], C[17], f[17][17], cost[17][17], val[17]; inline int read(){ int r = 0, z = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') z = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){r = r * 10 + ch - '0'; ch = getchar();} return r * z; } int Max(int a, int b){return a > b ? a : b;} int Min(int a, int b){return a < b ? a : b;} int DP(){ int res = inf; for(int i = 1; i <= m; i ++){ val[i] = 0; for(int j = 1; j < r; j ++) val[i] += abs(a[C[j]][i] - a[C[j + 1]][i]); } memset(cost, 0, sizeof(cost)); for(int i = 1; i <= m; i ++) for(int j = i + 1; j <= m; j ++){ cost[i][j] = 0; for(int k = 1; k <= r; k ++) cost[i][j] += abs(a[C[k]][i] - a[C[k]][j]); } for(int i = 0; i <= m; i ++) f[i][0] = 0; for(int i = 1; i <= m; i ++){ for(int k = 1; k <= Min(i, c); k ++){ f[i][k] = inf; for(int j = k - 1; j < i; j ++) f[i][k] = Min(f[i][k], f[j][k - 1] + cost[j][i] + val[i]); } } for(int i = c; i <= m; i ++) res = Min(res, f[i][c]); return res; } void dfs(int u, int cnt){ if(u > n){ //cout<<1<<endl; if(cnt == r) ans = Min(ans, DP()); return ; } dfs(u + 1, cnt); C[cnt + 1] = u; dfs(u + 1, cnt + 1); } void init(){ n = read(), m = read(), r = read(), c = read(); for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) a[i][j] = read(); dfs(1, 0); printf("%d\n", ans); } int main(){ init(); return 0; }
转载请注明原文地址: https://www.6miu.com/read-65613.html

最新回复(0)