[洛谷P2258][NOIP2014PJ]子矩阵(dfs)(dp)
NOIP 2014普及组 T4(话说一道PJ组的题就把我卡了一个多小时诶)
这道题在我看第一次的时候是没有意识到这是一道DP题的,然后就摁着DFS敲了好长时间,结果敲了一个TLE
这是DP!!!
下面开始进入正题
题目描述
给出如下定义:
- 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵。
例如,下面左图中选取第22、44行和第22、44、55列交叉位置的元素得到一个2 \times 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 \times 32×3的子矩阵是
4 7 4
8 6 9
相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的。
矩阵的分值:矩阵中每一对相邻元素之差的绝对值之和。
本题任务:给定一个nn行mm列的正整数矩阵,请你从这个矩阵中选出一个rr行cc列的子矩阵,使得这个子矩阵的分值最小,并输出这个分值。
输入输出格式
输入格式:
第一行包含用空格隔开的四个整数n,m,r,cn,m,r,c,意义如问题描述中所述,每两个整数之间用一个空格隔开。
接下来的nn行,每行包含mm个用空格隔开的整数,用来表示问题描述中那个nn行mm列的矩阵。
输出格式:
一个整数,表示满足题目描述的子矩阵的最小分值。
样例:
输入
输出
这道题就是将题目所给你的矩阵进行“选取”行与列的操作,从而得到所求的最大值的集合,但是重要的是我们不知道题目所给的行和列的值是多少,因此我们可以对此进行一个循环中的判断操作,在选出来这个行之后再去考虑列的情况,从而得出最优决策。
这个地方还有一个降维操作,就是我们在一个r * m(事先以及决定了选取那几行,该去考虑列的问题时)的矩阵中选取c列,这个时候我们可以把二维降低到一维,(因为这其中行已经有了判断的标准,我们只需要进行列的操作就足够了).
下面开始讲DP过程:
我们设f[i][j]表示在这个r*m的矩阵中,在其前i列中选择j列(且选的列中包括第i列),组成的子矩阵中,最小值(即其相邻元素的差的绝对值的和的最小值(之后的值等表达也是指的这个东西,即题目要求求出的值))是多少。
这样,推出状态转移方程如下:
f[i][j] = min (f[k][j-1] + hc[i][k] + lc[i])
下面就都是一些细节上的优化,代码里面已经讲的很清楚了。
Code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,r,c,gs=,minn=0x7fffffff,cmin; //不可能选取0列
int a[][];
int ch[],hc[][],lc[]; //hc[i][j] 对于第i列与第j列之间,所有同一行元素的差的绝对值的和
//lc[i]指的是第i列中所有元素的值
//ch[]是在dfs过程的数组
int f[][];
void init() //初始化
{
for(int i=;i<=m;i++)
{
lc[i] = ;
for(int j=;j<r;j++)
lc[i] += abs(a[ch[j]][i] - a[ch[j + ]][i]); //注意是+=
}
for(int i=;i<=m;i++)
{
for(int j=;j<i;j++) //注意j<i
{
hc[i][j] = ; //注意初始化
for(int k=;k<=r;k++)
hc[i][j] += abs(a[ch[k]][i] - a[ch[k]][j]); //注意是+=
}
}
}
void dp()
{
for(int i=;i<=m;i++)
{
cmin = min(i,c);
for(int j=;j<=cmin;j++)
{
if(j == ) //这个边界是只选取一列,就把元素赋进去就好
f[i][j] = lc[i];
else
if(j == i) //这个边界是前i列都需要选取
f[i][j] = f[i - ][j - ] + lc[i] + hc[i][j - ];
else
{
f[i][j] = 0x7fffffff; //注意初始化
for(int k=j-;k<i;k++)
f[i][j] = min(f[i][j],f[k][j - ] + lc[i] + hc[i][k]); //取最小值
}
if(j == c) //如果这种状态存在,那我们就更新一下
minn = min(minn,f[i][c]);
}
}
}
void dfs(int rt)
{
if(rt > n) //
{
init();
dp();
return ;
}
if(r - gs + == n - rt + ) //敲黑板!
/*
这个地方是一个剪枝,主要优化在了果rt和rt以后的元素必须全部取完,才能满足刚好有r个的条件,
则必须rt,否则便会取到少于r个元素的情况。
这样我们保证了rt > n时所有情况都刚好有r个,
*/
{
ch[gs++] = rt; //注意是gs++而不是++gs
dfs(rt + );
ch[gs--] = ; //注意是gs--而不是--gs
return ;
}
dfs(rt + );
if(gs <= r) //如果已经选取满了
{
ch[gs++] = rt; //但是还是需要这一步
dfs(rt + );
ch[gs--] = ;
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&r,&c);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&a[i][j]); //输入初始矩阵
dfs(); //开始搜索
printf("%d",minn); //答案已经被更新,输出就行
return ;
}
[洛谷P2258][NOIP2014PJ]子矩阵(dfs)(dp)的更多相关文章
- 【洛谷P2258】子矩阵
子矩阵 题目链接 搜索枚举选了哪几行,将DP降为一个一维的问题, 先预处理出w[i]表示该列上下元素差的绝对值之和 v[i][j]为第i列和第j列对应元素之差的绝对值之和 f[i][j]表示前j列中选 ...
- 题解 洛谷P2258 【子矩阵】
应该很容易想到暴力骗分. 我们考虑暴力\(dfs\)枚举所有行的选择,列的选择,每次跑一遍记下分值即可. 时间复杂度:\(O(C_n^r \times C_m^c \times r \times c) ...
- 洛谷 P5279 - [ZJOI2019]麻将(dp 套 dp)
洛谷题面传送门 一道 dp 套 dp 的 immortal tea 首先考虑如何判断一套牌是否已经胡牌了,考虑 \(dp\).我们考虑将所有牌按权值大小从大到小排成一列,那我们设 \(dp_ ...
- NOIP2014 T4 子矩阵 dfs+dp
最近在狂补题啊QAQ... 打算先把NOIP的干掉吧... 点我看题 链接还是放洛谷的了... 题意:给一个n*m的矩阵,在这个矩阵里选 r 行 c 列,然后这 r 行 c 列所相交的格子为新矩阵的, ...
- 洛谷 P2392 kkksc03考前临时抱佛脚, dp / 深搜
题目链接 P2392 kkksc03考前临时抱佛脚 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目 dp代码 #include <iostream> #includ ...
- 洛谷2344 奶牛抗议(DP+BIT+离散化)
洛谷2344 奶牛抗议 本题地址:http://www.luogu.org/problem/show?pid=2344 题目背景 Generic Cow Protests, 2011 Feb 题目描述 ...
- Lightning Conductor 洛谷P3515 决策单调性优化DP
遇见的第一道决策单调性优化DP,虽然看了题解,但是新技能√,很开森. 先%FlashHu大佬,反正我是看了他的题解和精美的配图才明白的,%%%巨佬. 废话不多说,看题: 题目大意 已知一个长度为n的序 ...
- 洛谷P1541 乌龟棋(四维DP)
To 洛谷.1541 乌龟棋 题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游 ...
- 【洛谷】P1052 过河【DP+路径压缩】
P1052 过河 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙 ...
随机推荐
- 闭包传递(floyed)
题目链接: https://cn.vjudge.net/contest/66569#problem/H 题目大意: n代表母牛的个数,m代表给定的信息的组数.每一组数包括a,b. 代表b崇拜a(突然发 ...
- android的五个进程优先级,内存不足时被清理的顺序
Android操作系统尝试尽可能长时间的保持应用的进程,但当可用内存很低时最终要移走一部分进程.怎样确定那些程序可以运行,那些要被销毁,Android让每一个进程在一个重要级的基础上运行,重要级低的进 ...
- shell-检测服务是否运行,并记日志
目的:每隔*分钟检测服务是否运行:若运行中,则记录执行的进程名称:若不运行,记录当前时间 shell: #!/bin/bash date=`date +%Y%m%d` log=/home/mono_$ ...
- 一个有趣的小例子,带你入门协程模块-asyncio
一个有趣的小例子,带你入门协程模块-asyncio 上篇文章写了关于yield from的用法,简单的了解异步模式,[https://www.cnblogs.com/c-x-a/p/10106031. ...
- Android主题切换
一.APK文件方式:(CM9方案) 我们知道Android给每个APK进程分配一个单独的用户空间,其manifest中的userid就是对应一个Linux用户的(Android 系统是基于Linux) ...
- 罗克韦尔 Allen-Bradley MicroLogix 1400 查看、设置IP
=============================================== 2019/4/14_第1次修改 ccb_warlock == ...
- Ex 6_18 硬币有限的兑换问题_第七次作业
子问题定义: 定义一个二维数组b,其中b[i][j]表示前i个币种是否能兑换价格j,表示第i个币种的面值,第i个币种的使用有两种情况,若使用,则b[i][j]=b[i-1][j-],若不使用,则b[i ...
- AndroidStudio、gradle、buildToolsVersion关系
使用AndroidStudio 开发也已经2年了,每次gradle 或者studio 有推荐更新后,项目重新sync后都会报错,提示更新相应的其他版本,比如AndroidStudio.gradle.b ...
- javascript判断是用什么设备打开
var userAgentInfo = navigator.userAgent //查看浏览器用于 HTTP 请求的用户代理头的值 var agents = ["Android", ...
- 使用exundelete在Linux下恢复删除的文件
原文:https://my.oschina.net/looly/blog/261912 Linux下执行 rm 并不会真正删除,而是将inode节点中的扇区删除,同时释放数据块.在数据块被系统重新分配 ...