好题
luogu P2258 [NOIP2014 普及组] 子矩阵
题意
给定一个 \(n\times m\) 的矩阵,求出选出 \(r\) 行,\(c\) 列的交叉组成的矩形的邻块差值和的最小值。
解法
考虑暴力,可以获得 \(55\) 分的高分。考虑优化一部分来优化时间复杂度,发现 \(16 \choose 8\) 其实并不是很大,大概是 \(10^4\) 的。于是我们考虑枚举列,剩下的再处理。考虑选择一行造成的影响,对于选择上一行,造成的影响是每一列元素对于上一行的差(以下称行差)加上本行列之间的差(以下称列差)。于是根据以上考虑 DP 即可。
设 \(f_{i,j}\) 表示选第 \(i\) 行,目前已经选了 \(j\) 行的最小邻块差值,\(cost_{i,j}\) 为第 \(i\) 行与第 \(j\) 行的行差,\(v_{i}\) 为第 \(i\) 的列差。有以下状态转移方程:
\[\begin{aligned}
f_{i,j}=\min\{f_{k,j-1}+cost_{i,k}(k < i)\}+v_{i}
\end{aligned}
\]
其中,\(cost\) 的预处理是 \(n^3\) 的。
时间复杂度 \(O(n^3{m \choose c})\),最坏大概 \(10^7\),可以通过。
#include <bits/stdc++.h>
using namespace std;
const int N=20,INF=0x3f3f3f3f;
int a[N][N],f[N][N],cost[N][N],v[N];
int n,m,r,c,ans=INF;
bool st[N];
vector<int> choose;
void dp() {
memset(f,0x3f,sizeof f);
for(int i=1;i<=n;i++) f[i][1]=v[i];
for(int i=1;i<=n;i++)
for(int j=2;j<=r;j++) {
for(int k=1;k<i;k++) f[i][j]=min(f[i][j],f[k][j-1]+cost[i][k]);
f[i][j]+=v[i];
}
for(int i=1;i<=n;i++) ans=min(ans,f[i][r]);
}
void dfs(int u) {
if(u==c) {
memset(cost,0,sizeof cost);
memset(v,0,sizeof v);
vector<int> line[N];
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) if(st[j]) line[i].push_back(a[i][j]);
for(int j=1;j<i;j++) for(int k=0;k<c;k++) cost[i][j]+=abs(line[i][k]-line[j][k]);
for(int j=1;j<c;j++) v[i]+=abs(line[i][j]-line[i][j-1]);
}
dp();return;
}
int s;
if(choose.size()) s=choose.back()+1;
else s=1;
for(int i=s;i<=m;i++) {
choose.push_back(i);
st[i]=1;
dfs(u+1);
st[i]=0;
choose.pop_back();
}
}
signed main() {
cin>>n>>m>>r>>c;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j];
dfs(0);
cout<<ans;
return 0;
}
luogu P1983 [NOIP2013 普及组] 车站分级
思路
实际上,题目的要求就是裸的差分约束,但是如果差分约束,时间复杂度是不能接受的,\(O(mn^2)\)。于是考虑优化,