P2258 子矩阵——搜索+dp

P2258 子矩阵

二进制枚举套二进制枚举能过多一半的点;

我们只需要优化一下第二个二进制枚举的部分;

首先我们先枚举选哪几行,再预处理我们需要的差值,上下,左右;

sum_shang,sum_heng

然后DP查找最小值

dp[i][j]表示前i列已经选了j列;

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20;
int n,m,r,c;
int a[maxn][maxn];
int id[maxn];
int b[maxn];
int sum_s[maxn];
int sum_h[maxn][maxn];

void pre_pare()
{
    memset(sum_s,0,sizeof(sum_s));
    int num=0;
    for(int i=1;i<=n;i++) if(b[i]) id[++num]=i;
    for(int i=1;i<r;i++)
    {
        for(int j=1;j<=m;j++)
        {
            sum_s[j]+=abs(a[id[i]][j]-a[id[i+1]][j]);
        }
    }
    
    for(int i=1;i<=m;i++)
    {
        for(int j=i+1;j<=m;j++)
        {
            sum_h[i][j]=0;
            for(int k=1;k<=r;k++)
            {
                sum_h[i][j]+=abs(a[id[k]][i]-a[id[k]][j]);
            }
        }
    }
    
}

int ans=2147483647,res=2147483647;
int dp[maxn][maxn];

int query()
{
    memset(dp,0x3f,sizeof(dp));
    res=2147483647;
    for(int i=1;i<=m;i++)
    {
        dp[i][1]=sum_s[i];
        for(int j=2;j<=c;j++)
        {
            for(int k=1;k<i;k++)
            {
                dp[i][j]=min(dp[i][j],dp[k][j-1]+sum_s[i]+sum_h[k][i]);
            }
        }
        res=min(res,dp[i][c]);
    }
    return res;
}

void dfs(int x,int sum)
{
    if(sum>r) return ;
    if(x==n+1)
    {
        if(sum!=r) return ;
        pre_pare();
        ans=min(ans,query());
        return ;
    }
    b[x]=0;
    dfs(x+1,sum);
    b[x]=1;
    dfs(x+1,sum+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",&a[i][j]);
        }
    }
    dfs(1,0);
    printf("%d",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/WHFF521/p/11675228.html