应该很容易想到暴力骗分。

我们考虑暴力\(dfs\)枚举所有行的选择,列的选择,每次跑一遍记下分值即可。

时间复杂度:\(O(C_n^r \times C_m^c \times r \times c)\)

可以水过\(60pts\)。

#include<bits/stdc++.h>
#define INF 1000000007
using namespace std;
inline int read(){
register int s=0,f=1;
register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f*=-1;ch=getchar();}
while(isdigit(ch))s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return s*f;
}
const int N=20;
int n,m,r,c,ans=INF;
int a[N][N],fh[N],fl[N];
int solve(){
int sum=0;
for(int i=1;i<=r;i++){
for(int j=1;j<c;j++){
sum+=abs(a[fh[i]][fl[j]]-a[fh[i]][fl[j+1]]);
}
}
for(int j=1;j<=c;j++){
for(int i=1;i<r;i++){
sum+=abs(a[fh[i]][fl[j]]-a[fh[i+1]][fl[j]]);
}
}
return sum;
}
void dfsl(int dep,int cnt){
if(cnt>c){ans=min(ans,solve());return;}
if(dep>m)return;
fl[cnt]=dep;dfsl(dep+1,cnt+1);
fl[cnt]=0;dfsl(dep+1,cnt);
}
void dfsh(int dep,int cnt){
if(cnt>r){dfsl(1,1);return;}
if(dep>n)return;
fh[cnt]=dep;dfsh(dep+1,cnt+1);
fh[cnt]=0;dfsh(dep+1,cnt);
}
int main(){
ios::sync_with_stdio(false);
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();
}
}
dfsh(1,1);
cout<<ans<<endl;
return 0;
}

【算法分析】

我们依然先来枚举行的选择,于是接下来问题就转化成了:

  • 一个\(r \times m\)的矩阵选\(c\)列,使其分值最小。

显然,这是一个\(01\)选择列的问题,所以我们直接\(dp\)就好了。

设状态\(dp[i][j]\)表示当前选择第\(i\)列的同时已经选择了\(j\)列的最小分数。

接下来我们预处理好两个东西:

  • \(up[i]\)表示在当前行选择下第\(i\)列的总分值。

  • \(num[i][j]\)表示在当前行选择下连接第\(i\)列和第\(j\)列的分值。

那么对于第\(dp[i][j]\),我们只需再枚举一个第\(k\)列。当连接第\(k\)列和第\(i\)列时,价值即为本身第\(i\)列的分值\(up[i]\)和连接两列的分值\(num[i][k]\)。

于是不难推出:

\[dp[i][j]=min\{ dp[k][j-1]+up[i]+num[k][i] \}
\]

\[i \in [1,m],j\in[2,min(i,c)],k \in[1,i)
\]

边界处理:

\[dp=\{0x7f\},dp[i][1]=up[i]
\]

更新最小分值:

\[ans=min\{dp[i][c]\}
\]

时间复杂度:\(O(C_n^r \times m^2n)\)

#include<bits/stdc++.h>
#define INF 1000000007
using namespace std;
inline int read(){
register int s=0,f=1;
register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f*=-1;ch=getchar();}
while(isdigit(ch))s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return s*f;
}
const int N=20;
int n,m,r,c,ans=INF;
int a[N][N],f[N],up[N],num[N][N],dp[N][N];
void solve(){
memset(up,0,sizeof(up));
memset(num,0,sizeof(num));
for(int j=1;j<=m;j++){
for(int i=1;i<r;i++){
up[j]+=abs(a[f[i]][j]-a[f[i+1]][j]);
}
}
for(int i=1;i<=m;i++){
for(int j=i+1;j<=m;j++){
for(int k=1;k<=r;k++){
num[i][j]+=abs(a[f[k]][i]-a[f[k]][j]);
}
}
}
memset(dp,0x7f,sizeof(dp));
int tot=INF;
for(int i=1;i<=m;i++){
dp[i][1]=up[i];
for(int j=2;j<=min(i,c);j++){
for(int k=1;k<i;k++){
dp[i][j]=min(dp[i][j],dp[k][j-1]+up[i]+num[k][i]);
}
}
tot=min(tot,dp[i][c]);
}
ans=min(ans,tot);
}
void dfs(int dep,int cnt){
if(cnt>r){solve();return;}
if(dep>n)return;
f[cnt]=dep;dfs(dep+1,cnt+1);
f[cnt]=0;dfs(dep+1,cnt);
}
int main(){
ios::sync_with_stdio(false);
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,1);
cout<<ans<<endl;
return 0;
}

\(\operatorname{Update}\) \(\operatorname{On}\) \(\operatorname{2019.10.29}\)

题解 洛谷P2258 【子矩阵】的更多相关文章

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

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

  2. 洛谷P2258 子矩阵

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

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

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

  4. 洛谷P2258 子矩阵 题解 状态压缩/枚举/动态规划

    作者:zifeiy 标签:状态压缩.枚举.动态规划 题目链接:https://www.luogu.org/problem/P2258 这道题目状态压缩是肯定的,我们需要用二进制来枚举状态. 江湖上有一 ...

  5. 洛谷 P2258 子矩阵

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

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

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

  7. 题解 洛谷P5018【对称二叉树】(noip2018T4)

    \(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...

  8. 题解 洛谷 P3396 【哈希冲突】(根号分治)

    根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...

  9. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

随机推荐

  1. 基于Prometheus+Grafana+AlertManager的监控系统

    一.Prometheus 1.1 简介 Prometheus是一套开源的监控&报警&时间序列数据库的组合,基于应用的metrics来进行监控的开源工具 . 1.2 下载&安装 ...

  2. 最新 淘友天下java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.淘友天下等10家互联网公司的校招Offer,因为某些自身原因最终选择了淘友天下.6.7月主要是做系统复习.项目复盘.Leet ...

  3. 通过names.index()方法找到第2个eva值 ,并将其改成EVA

    names= ['alex','rain','peiqi','eva','mac','jack','eva','kangkang','jain']first_index=names.index('ev ...

  4. ASP.NET MVC请求参数字符串之区分空与NULL

    开发中经常会写增删改查的功能,这里记录下在更新操作时遇到的一个问题. 假设一个模型对应数据库中某一张表,在更新时便需要区分是一次性更新全部字段还是仅更新部分字段.希望能做到传递某个参数时便更新,未传递 ...

  5. docker 部署 nsq

    这篇文章主要介绍如何使用docker部署 nsq 组件 环境准备 本文基于一台 ubuntu 虚拟机试验 docker 安装 docker 安装方式 使用docker version 命令检查 doc ...

  6. Spring Cloud--实现Eureka的高可用(Eureka集群搭建)实例

    将10086注册到10087上: 再在10086服务的基础上复制一个Eureka的服务,端口为10087,将其注册到10086上: application-name的名称保持一致,只是一个服务的两个实 ...

  7. TweenMax参数用法中文介绍

    TweenMax 建立在 TweenLite 和 TweenFilterLite 基础之上,因此,又揉合了这二者的功能,使得功能更加的齐备,但是如果说易用性,觉得还是 TweenLite 来得方便一些 ...

  8. typescript之基础类型

    基础类型分为:数字.字符串.数组.元组.枚举.Any.Object.Null.Undefined.Never.Void 各种类型写法如下: 1.数字(number) let num:number = ...

  9. iOS - 小功能 跳转到淘宝或天猫的商品展示详情页

    最近做iOS项目的时候遇到一个需求,点击广告页,跳转到淘宝或天猫的商品详情页. 具体需要是这样: 1)安装了淘宝:跳转到淘宝详情页. 2)没装淘宝,装了天猫:跳转到天猫详情页 3)淘宝.天猫都没装:跳 ...

  10. Java RMI学习

    网上资料: Java RMI Tutorial Dynamic code downloading using RMI RPC-维基:Remote procedure call implementing ...