洛谷 P2258 NOIP2014普及组 T4 子矩阵 DFS+DP

针对NOIP2014普及组的题目,面对1≤n, m≤16的数据范围,通过DFS搜索结合DP降维优化,将时间复杂度从2^(n+m)降低到m^3。在确定行后,利用列的DP计算每列间的得分,将二维问题转化为一维。f[i,j]表示以i列结尾,共j列的最小得分,由lc[k,i](第k列和第i列横向差)和rc[i](第i列上下差)计算得出。" 132755648,11274034,铝电解电容的关键特性解析,"['嵌入式硬件', '硬件工程', '电容特性', '电子元件', '电路设计']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述
数据范围:1≤n≤16,1≤m≤16,矩阵中每个元素1≤a[i][j]≤1000,1≤r≤n,1≤c≤m

看到数据范围,行与列的范围只有16,首先想到搜索,枚举取舍,时间复杂度达到O(2^(n+m)) ,时间承受不了
对于多维问题,常见的思路是降维
在枚举行之后,对列可以DP。
在选完行以后,每一列上行与行之间的得分就可以计算了,列与列之间的得分即为两列上横向差值的绝对值,这样就把二维压缩成一维了。
2^m -> m^3 时间复杂度骤减
f[i,j]=f[k,j-1]+lc[k,i]+rc[i]
f[i,j]表示以i列结尾,共j列的最小得分
lc[k,i]表示第k列和第i列的横向的差
rc[i]表示第i列上下元素的差
(是我太笨想不到先搜再DP T_T)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#define min(a,b) (a<b?a:b)
using namespace std;
int a[20][20],m,n,r,c,ans=2147483647;
int xx[20],rc[20],lc[20][20],f[20][20];
void dp(){
    memset(f,0x7f,sizeof f);
    memset(rc,0,sizeof rc);
    memset(lc,0,sizeof lc);
    for(int i=1;i<=m;i++)
        for(int j=1;j<i;j++)
            for(int k=1;k<=r;k++)
                lc[j][i]+=abs(a[xx[k]][i]-a[xx[k]][j]);
    for(int i=1;i<=m;i++)
        for(int j=1;j<r;j++)
            rc[i]+=abs(a[xx[j]][i]-a[xx[j+1]][i]);
    for(int i=1;i<=m;i++) f[i][0]=0,f[i][1]=rc[i];
    for(int i=1;i<=c;i++)
        for(int j=i;j<=m;j++)
            for(int k=i-1;k<j;k++)
                f[j][i]=min(f[j][i],f[k][i-1]+rc[j]+lc[k][j]);
    for(int i=c;i<=m;i++) ans=min(ans,f[i][c]);
    return ;
}
void dfs(int x,int las){//已经选了x行
    if(x==r) { dp(); return; }
    if(r-x>las) return;
    for(int i=las;i>=1;i--) xx[r-x]=i,dfs(x+1,i-1);
    return ;
}
int main(){
    freopen("submatrix.in","r",stdin);
    freopen("submatrix.out","w",stdout);
    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(0,n);
    printf("%d",ans);
    fclose(stdin); fclose(stdout);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值