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

洛谷 P2258 NOIP2014普及组 T4 子矩阵 DFS+DP_第1张图片
数据范围: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 
#include 
#include 
#define min(a,b) (a
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;jfor(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;jabs(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;k1]+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;
}

你可能感兴趣的:(DFS优化)