• 题解 P2258 【子矩阵】


    题目大意

    Solution [NOIP2014普及]子矩阵

    题目大意:定义一个矩阵的分值为两两相邻元素的差的绝对值,试在一个(n)(m)列的矩阵中选出一个(r)(c)列的子矩阵(即行列交叉位置的元素),使其分值最小

    动态规划,搜索


    题目分析:一开始拿到这道题想的是爆搜,但是分析了一下时间复杂度(O(C_n^rC_m^c))怕是有点悬.找着题意(dp)?,四维(dp)写不出来在下告辞

    然后就有一种神奇的做法,先(dfs)枚举行,然后就会得到很多(r)(m)列的矩阵,然后再用二维(dp)求解

    (f[i][j])表示前(i)列,当前已经选了(j)列的最小分值.设(w[i])表示(i)这一列的分值,(v[i][j])表示(i,j)这两列的分值,那么

    [f[i][j] = min{f[k][j - 1] + w[i] + v[k][i] quad | quad j - 1 leq k < i} ]

    通过一些技巧来处理可以不用复杂的(if)语句来考虑边界条件,让程序更加简洁.即假设有一个虚拟的第(0)

    分析一下复杂度?枚举行为(O(C_n^r)),进行一次(dp)的成本为(O(m^2))

    所以总复杂度为(O(C_n^rm^2)),跑的过去(我常数有那么爆炸吗,吸口氧运行时间变成了原来的(frac{1}{4}))

    代码奉上

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int maxn = 32;
    int n,m,r,c,p[maxn],w[maxn],v[maxn][maxn],f[maxn][maxn],val[maxn][maxn],ans = 1e9;
    inline void dp(){//进行dp
        for(int i = 1;i <= m;i++)//初始化
            for(int j = 1;j <= m;j++)
                f[i][j] = 1e9;
        for(int i = 1;i <= m;i++){//计算每一列的分值
            w[i] = 0;
            for(int j = 2;j <= r;j++)
                w[i] += abs(val[p[j]][i] - val[p[j - 1]][i]);
        }
        for(int i = 1;i <= m;i++)//初始化
            for(int j = 1;j <= m;j++)
                v[i][j] = 0;
        for(int i = 1;i <= m;i++)//计算两两列之间的分值
            for(int j = 1;j <= m;j++)
                for(int k = 1;k <= r;k++)
                    v[i][j] += abs(val[p[k]][i] - val[p[k]][j]);
        for(int i = 1;i <= m;i++)//进行dp
            for(int j = 1;j <= min(i,c);j++){
                for(int k = j - 1;k < i;k++)
                        f[i][j] = min(f[i][j],f[k][j - 1] + w[i] + v[k][i]);
                if(j == c)ans = min(ans,f[i][j]);//如果已经选了r行c列了,更新答案
            }
    }
    inline void dfs(int line,int step){//dfs枚举行
        if(step == r + 1){
            dp();
            return;
        }
        if(line > n)return;
        p[step] = line;
        dfs(line + 1,step + 1);
        dfs(line + 1,step);
    }
    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",&val[i][j]);
        dfs(1,1);
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    JS 数组
    JS 模拟彩票
    C++ 动态内存
    计算机网络--OSI七层模型
    C++ 异常处理
    C++ 文件和流
    数据库学习教程网站
    数据结构--哈夫曼树
    数据结构--红黑树
    数据结构--伸展树
  • 原文地址:https://www.cnblogs.com/colazcy/p/11514983.html
Copyright © 2020-2023  润新知