• 作者:zifeiy
  • 标签:状态压缩、枚举、动态规划

题目链接:https://www.luogu.org/problem/P2258

这道题目状态压缩是肯定的,我们需要用二进制来枚举状态。

江湖上有一句话,叫做“暴力出奇迹”,所以我一开始是暴力枚举的。

暴力枚举50分

下面是我暴力枚举(骗分50)的思路(后续动态规划的思想也是建立在此基础之上,所以最好还是了解一下)。

首先用二进制枚举所有选择r行的行的排列,然后用二进制枚举所有选择c列的排列,然后计算选中了这r行c列的结果,与最终答案比较。

时间复杂度为 \(O( 2^n \times 2^m \times n \times m ) = O(2^{40})\) ,会超时,过了50%数据。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 22;
int n, m, r, c, a[maxn][maxn], b[maxn][maxn], ans = -1;
// 枚举行和列,但是这样只能过50%数据,另50%数据会TLE
void handle() {
int tmp = 0;
for (int i = 0; i < r; i ++) for (int j = 0; j < c; j ++) {
if (i) tmp += abs(b[i][j] - b[i-1][j]);
if (j) tmp += abs(b[i][j] - b[i][j-1]);
}
if (ans == -1 || ans > tmp) {
ans = tmp;
}
}
int main() {
scanf("%d%d%d%d", &n, &m, &r, &c);
for (int i = 0; i < n; i ++) for (int j = 0; j < m; j ++) scanf("%d", &a[i][j]);
for (int s1 = 0; s1 < (1<<n); s1 ++) {
if (__builtin_popcount(s1) != r) continue;
for (int s2 = 0; s2 < (1<<m); s2 ++) {
if (__builtin_popcount(s2) != c) continue;
int id1 = 0;
for (int i = 0; i < n; i ++) {
if (s1 & (1<<i)) {
int id2 = 0;
for (int j = 0; j < m; j ++) {
if (s2 & (1<<j)) {
b[id1][id2++] = a[i][j];
}
}
id1 ++;
}
}
handle();
}
}
printf("%d\n", ans);
return 0;
}

枚举+DP100分

首先还是二进制枚举所有选择了r行的排列,然后对于选中的r行,我们定义状态 \(f[i][j]\) 表示到第\(i\)列(且包含第\(i\)列)并且此时选择了 \(j\) 列的最小值。

则我们只需要遍历每一列 \(i\) ,对于列号 \(i\) :

  • \(f[i][0] = 0\) ;
  • \(f[i][1] = t1\) (这里的 \(t1\) 就是选中的r行中元素在第 \(j\) 列的所有元素两两绝对值之和);

然后我们再从 \(0\) 到 \(i-1\) 遍历列号 \(j\) ,再从 \(2\) 到 \(\min (c, i+1)\) 遍历 \(k\) ,有状态转移方程:

\(f[i][k] = min(f[i][k], f[j][k-1] + t1 + t2)\) 。(其中 \(t2\) 表示第 \(i\) 列 和第 \(j\) 列 r对元素绝对值之差的和)。

这样,时间复杂度降到了 \(O( 2^n \times n^3 ) = O( 2^28 )\) ,这样能够过所有100%数据。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 22;
const int INF = (1<<29);
int n, m, r, c, a[maxn][maxn], id[maxn], f[maxn][maxn], ans = -1;
void handle() {
for (int i = 0; i < m; i ++) for (int j = 2; j <= c; j ++) f[i][j] = INF;
for (int i = 0; i < m; i ++) {
int t1 = 0;
for (int j = 1; j < r; j ++) t1 += abs(a[ id[j-1] ][i] - a[ id[j] ][i]);
f[i][0] = 0;
f[i][1] = t1;
for (int j = 0; j < i; j ++) {
int t2 = 0;
for (int k = 0; k < r; k ++) t2 += abs(a[ id[k] ][j] - a[ id[k] ][i]);
for (int k = 2; k <= i+1 && k <= c; k ++)
f[i][k] = min(f[i][k], f[j][k-1] + t1 + t2);
} }
for (int i = c-1; i < m; i ++)
if (ans == -1 || ans > f[i][c]) ans = f[i][c];
}
int main() {
scanf("%d%d%d%d", &n, &m, &r, &c);
for (int i = 0; i < n; i ++) for (int j = 0; j < m; j ++) scanf("%d", &a[i][j]);
for (int s = 0; s < (1<<n); s ++) {
if (__builtin_popcount(s) != r) continue;
int cnt = 0;
for (int i = 0; i < n; i ++) if (s & (1<<i)) id[cnt++] = i;
handle();
}
printf("%d\n", ans);
return 0;
}

洛谷P2258 子矩阵 题解 状态压缩/枚举/动态规划的更多相关文章

  1. 洛谷P2258 子矩阵——题解

    题目传送 表示一开始也是一脸懵逼,虽然想到了DP,但面对多变的状态不知从何转移及怎么合理记录状态.之(借鉴大佬思路)后,豁然开朗,于是在AC后分享一下题解. 发现数据范围出奇地小,不过越是小的数据范围 ...

  2. 洛谷 P2258 子矩阵 解题报告

    P2258 子矩阵 题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第 2 . 4行和第 ...

  3. 洛谷P2258 子矩阵

    P2258 子矩阵 题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4 ...

  4. 洛谷 P2258 子矩阵

    题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4.5列交叉位置的元素 ...

  5. 【Luogu】P2258子矩阵(状态压缩,DP)

    233今天蒟蒻我连文化课都没听光想着这个了 然后我调了一下午终于过了!!! 一看数据范围似乎是状压,然而216等于65536.开一个65536*65536的二维数组似乎不太现实. 所以Rqy在四月还是 ...

  6. 洛谷P2258 子矩阵[2017年5月计划 清北学堂51精英班Day1]

    题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4.5列交叉位置的元素 ...

  7. 洛谷P1357 花园(状态压缩 + 矩阵快速幂加速递推)

    题目链接:传送门 题目: 题目描述 小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(<=N<=^).他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻 ...

  8. 洛谷P1036 选数 题解 简单搜索/简单状态压缩枚举

    题目链接:https://www.luogu.com.cn/problem/P1036 题目描述 已知 \(n\) 个整数 \(x_1,x_2,-,x_n\) ,以及 \(1\) 个整数 \(k(k& ...

  9. UVA 1508 - Equipment 状态压缩 枚举子集 dfs

    UVA 1508 - Equipment 状态压缩 枚举子集 dfs ACM 题目地址:option=com_onlinejudge&Itemid=8&category=457& ...

随机推荐

  1. 洛谷 1083 (NOIp2012) 借教室——标记永久化线段树 / 差分+二分

    题目:https://www.luogu.org/problemnew/show/P1083 听说线段树不标记永久化会T一个点. 注意mn记录的是本层以下.带上标记的min! #include< ...

  2. selenium(2):环境搭建完成后,初步运行遇到的问题

    检验是否搭建成功. . 问题一:运行时候,报错:请停用以开发者模式运行的扩展程序 出现错误如下: 原因:chromedriver的版本号过低了. 解决办法:应该安装与chrome版本对应的chrome ...

  3. 媒体查询(media):设置PC端网页居中显示

    @media screen and (min-width: 768px){ body{ background-color: #EAEAEA; } #fater{ width: 640px; margi ...

  4. PHP生成短连接的方法

    PHP生成短连接的方法.md PHP生成短连接的方法 直接贴上方法,函数可以查看手册. <?php /** 生成短网址 * @param String $url 原网址 * @return St ...

  5. CodePlus2017 12月月赛 div2可做题2

    11月的月赛错过了,来打12月月赛,由于很(zi)想(ji)拿(tai)衣(ruo)服(la),所以去打div2. T1是一个sb模拟,但是机房全卡死在这道语文题上了,基本上弄了一个半小时,T2可以秒 ...

  6. 廖雪峰Python总结3

    1.模块简介 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件中,这样每个文件包含的代码相对来说就比较少.一个.py文件就称之为一个模块(Module). 使用模块的好处: 提高了代码的可 ...

  7. 在maven多模块结构中,并且使用overlay的情况下使用jetty热部署

    在使用maven多模块的结构的时候,同时有多个web工程使用maven-war-plugin的overlay来组织的时候,本地开发时如何在eclipse里面启动容器并且可以热部署调试是个比较麻烦的问题 ...

  8. ls command not found

    编辑profile文件没有写正确,导致在命令行下 ls等命令不能够识别. 在命令行下打入下面这段就可以了 export PATH=/usr/local/sbin:/usr/local/bin:/sbi ...

  9. GenericServlet vs HttpServlet

     1>>>>>>>> Difference between HttpServlet vs Generic Severlet javax.servlet. ...

  10. scorllview嵌套gridview和listview的兼容问题

    ScrollView嵌套GridView或ListView,由于这两款控件都自带滚动条,当他们碰到一起的时候便会出问题,即GridView会显示不全. 解决办法:自定义一个GridView控件 pac ...