洛谷P2258 子矩阵

这是一道比较无聊的题目,虽然看上去极其的吓人,但是看完N是小于等于16之后,一切皆有可能
首先,要选出r行c列,我们分开做,暴力枚举出所有的行状态,对于每一种状态分别DP,最后更新答案即可

DP我是这么写的 f [ i ] [ j ] f[i][j] f[i][j]表示原图第 j j j列选了要求矩阵的第 i i i列,所得到矩阵的最小值
转移十分的显然 f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i − 1 ] [ ( i − 1 ) − > ( j − 1 ) ] + d e l t a ) f[i][j]=min(f[i][j],f[i-1][(i-1)->(j-1)]+delta) f[i][j]=min(f[i][j],f[i1][(i1)>(j1)]+delta),重点是delta怎么求
我维护了两个数组, l i s t list list表示,当前列内俩俩相邻数的差值和, l i n e [ i ] [ j ] line[i][j] line[i][j]表示 i i i列和 j j j列之间数的差值之和

转移就很清楚了,片段如下

memset(f,127,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<=c;i++){
		for(int j=i;j<=m;j++){
			for(int k=i-1;k<j;k++){
				f[i][j]=min(f[i][j],f[i-1][k]+list[j]+line[k][j]);
			}
		}
	}

啥,你问我 l i s t list list l i n e line line怎么维护,别问,问就暴力维护
毕竟 C ( n , n 2 ) ∗ n 3 2 C(n, \frac n 2 )*\frac {n^3} 2 C(n,2n)2n3最大才2500w左右

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 26;
int n,m,r,c,g[maxn][maxn],f[maxn][maxn];
int cho[maxn],ans=523454543,line[maxn][maxn],list[maxn];

void Checking(){
	memset(list,0,sizeof(list));
	memset(line,0,sizeof(line));
	for(int i=1;i<=m;i++){
		for(int j=1;j<=r;j++){
			int l=cho[j];
			list[i]+=abs(g[l][i]-g[cho[j-1]][i]);
			for(int k=i+1;k<=m;k++){
				line[i][k]+=abs(g[l][i]-g[l][k]);
			}
		}
		list[i]-=g[cho[1]][i];
	}
	memset(f,127,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<=c;i++){
		for(int j=i;j<=m;j++){
			for(int k=i-1;k<j;k++){
				f[i][j]=min(f[i][j],f[i-1][k]+list[j]+line[k][j]);
			}
		}
	}
	for(int i=c;i<=m;i++)ans=min(ans,f[c][i]);
}


void dfs(int x,int k){
	if(k==r){
		Checking();
		return ;
	}
	else{
		for(int i=x;i<=n;i++){
			cho[k+1]=i;
			dfs(i+1,k+1);
		}
	}
}



int main(){
	scanf("%d%d%d%d",&n,&m,&r,&c);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&g[i][j]);
	dfs(1,0);
	cout<<ans;
}

/*
3 3 3 3
1 2 3
6 5 4
2 3 4
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值