P2258 子矩阵
子矩阵
思路
很容易想到这是一个动态规划,但是从题目来分析,不能很容易的设计出状态转移方程。
原题为从矩阵中选出r行c列
我们可以将问题进行化简,先对于原题进行dfs枚举行数
对于每次枚举出来的r行m列,再进行一次动态规划
对于在r行m列中选出c列来,其转移方程就非常容易得到了
\[f[i][j] = min_(f[k][j-1]+w[i]+v[k][i])
\]
其中\(w\)数组是每列中元素的分值
而\(v[i][j]\)则是第\(i\)与第\(j\)之间的分值
\(f[i][j]\)是选第\(i\)列作为第\(j\)行最小值
\(f\)数组初始化为$$f[i][1]=w[i]$$
总程序的时间复杂度主要由两部分构成
dfs O(\(C_n^r\))
动态规划 O(\(m^3\))
总时间复杂度为O(\(C_n^rm^3\))
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int a[23][233];
int f[233][23];
int v[233][233];
int p[2333];
int w[2333];
int n,m;
int r,c;
int minn=1<<30;
inline void dtgh()
{
memset(f,0x7f,sizeof(f));
memset(w,0,sizeof(w));
memset(v,0,sizeof(v));
for(int i=1;i<=m;i++)
{
for(int j=2;j<=r;j++)
w[i]+=abs(a[p[j-1]][i]-a[p[j]][i]);
}
for(int i=1;i<=m;i++)
for(int j=i+1;j<=m;j++)
{
if(i == j) continue;
for(int t=1;t<=r;t++)
v[i][j]+=abs(a[p[t]][i]-a[p[t]][j]);
}
for(int i=1;i<=m;i++)
{
f[i][1]=w[i];
}
for(int i=1;i<=m;i++)
for(int j=1;j<=min(c,i);j++)
{
for(int k=j-1;k<i;k++)
f[i][j]=min(f[i][j],f[k][j-1]+w[i]+v[k][i]);
}
for(int i=1;i<=m;i++)
minn=min(minn,f[i][c]);
}
inline void dfs(int line,int step)
{
if(step == r+1)
{
dtgh();
return ;
}
if(line > n) return;
p[step]=line;
dfs(line+1,step+1);
dfs(line+1,step);
}
int main()
{
ios_base::sync_with_stdio(false);
cout.tie(NULL);
cin.tie(NULL);
cin>>n>>m;
cin>>r>>c;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
dfs(1,1);
cout<<minn;
}