https://www.acwing.com/problem/content/description/462/ 从矩阵中选一个子矩阵,按题目的计算方法使得权值最小; 先对选行的方法进行状压,这时每个选出来的子矩阵就是一个r*m的矩阵,计算行的前缀和和每两对列的差值和,因为行的选法已经确定,通过前缀和可以把这个矩阵看成一个一维的背包,按01背包的方法跑一边就行
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 20, INF = 1e9; int n, m, r, c; int matrix[N][N]; int f[N][N]; int cw[N], rw[N][N]; int q[N]; int count(int x) { int s = 0; for (int i = 0; i < n; i ++ ) s += x >> i & 1; return s; } int main() { cin >> n >> m >> r >> c; for (int i = 0; i < n; i ++ ) for (int j = 0; j < m; j ++ ) cin >> matrix[i][j]; int res = INF; for (int state = 0; state < 1 << n; state ++ ) if (count(state) == r) { for (int i = 0, j = 0; i < n; i ++ ) if (state >> i & 1) q[j ++ ] = i;//选了的位置 for (int i = 0; i < m; i ++ )//对每一列 { cw[i] = 0; for (int j = 1; j < r; j ++ )//统计选出来的列的每列的上下差 cw[i] += abs(matrix[q[j]][i] - matrix[q[j - 1]][i]); } for (int i = 0; i < m; i ++ )//当前压出来的行的第ij列每一行的差的和 for (int j = i + 1; j < m; j ++ ) { rw[i][j] = 0; for (int k = 0; k < r; k ++ ) rw[i][j] += abs(matrix[q[k]][i] - matrix[q[k]][j]); } for (int i = 0; i < m; i ++ )//背包 { f[i][1] = cw[i];//当前是第i个,选了1个 for (int j = 2; j <= c; j ++ ) { f[i][j] = INF; for (int k = 0; k < i; k ++ ) f[i][j] = min(f[i][j], f[ k][j - 1] + cw[i] + rw[k][i]); } res = min(res, f[i][c]); } } cout << res << endl; return 0; }