[NOIP2014] 子矩阵

题意:给你一个n×m的矩阵(n,m<=16),要你从中选r行c列,行和列的相交部分为该矩阵的子矩阵,一个子矩阵的分值为相邻元素的差的绝对值之和,你需要最小化子矩阵的分值

题解:

暴搜+dp

暴搜出列的选择方案,然后用dp决策行的选择方案

dp[i][j]表示选择了第i行,总共已经选了j行的最小分值,w[i]为i行的分值,v[i][k]为i行与k行相接后获得的分值

dp[i][j]=min(dp[i][j],dp[k][j-1]+v[i][k]+w[i])

 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define N 20
using namespace std;
 
int n,m,r,c,ans=1<<30;
int a[N],g[N][N],dp[N][N],w[N],v[N][N];
bool bj[N];
 
int gi() {
  int x=0,o=1; char ch=getchar();
  while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
  if(ch=='-') o=-1,ch=getchar();
  while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
  return o*x;
}
 
void cal() {
  memset(w,0,sizeof(w));
  memset(v,0,sizeof(v));
  memset(dp,0,sizeof(dp));
  int ret=1<<30;
  for(int i=1; i<=n; i++)
    for(int j=1; j<c; j++)
      w[i]+=abs(g[i][a[j]]-g[i][a[j+1]]);
  for(int i=0; i<=n; i++)
    for(int k=i+1; k<=n; k++)
      for(int j=1; j<=c; j++)
	v[i][k]+=abs(g[i][a[j]]-g[k][a[j]]);
  memset(dp,63,sizeof(dp));
  for(int i=1; i<=n; i++) dp[i][0]=0,dp[i][1]=w[i];
  for(int i=1; i<=n; i++)
    for(int k=i+1; k<=n; k++)
      for(int j=1; j<=i+1; j++)
	dp[k][j]=min(dp[k][j],dp[i][j-1]+w[k]+v[i][k]);
  for(int i=r; i<=n; i++)
    ret=min(ret,dp[i][r]);
  ans=min(ans,ret);
}
 
void dfs(int dep) {
  if(dep==c+1) {
    cal();
    return;
  }
  for(int i=a[dep-1]+1; i<=m; i++) {
    if(bj[i]) continue;
    bj[i]=1,a[dep]=i;
    dfs(dep+1);
    bj[i]=0,a[dep]=0;
  } 
} 
 
int main() {
  n=gi(),m=gi(),r=gi(),c=gi();
  for(int i=1; i<=n; i++)
    for(int j=1; j<=m; j++)
      g[i][j]=gi();
  dfs(1);
  printf("%d", ans);
  return 0;
}
posted @ 2017-10-05 23:40  HLX_Y  阅读(290)  评论(0编辑  收藏  举报