好题

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)\)。于是考虑优化,

posted @ 2024-10-07 10:25  PM_pro  阅读(1)  评论(0编辑  收藏  举报