码迷,mamicode.com
首页 > 其他好文 > 详细

【题解】 P2258 子矩阵

时间:2019-07-25 20:30:45      阅读:104      评论:0      收藏:0      [点我收藏+]

标签:pre   pac   题解   const   tchar   min   template   需要   string   

在矩阵中求解最优情况,其中$n \leq 16$

假设现在已经得到保留的行与列的编号,递推矩阵分值的复杂度是$O(n^2)$遍历一遍就ok

50pts 假设行列全部枚举全排列的话,枚举次数在最坏情况下是$(C_{12}^6)^2=924^2$,在加上求分值的复杂度则总复杂度还是勉强可以接受的?

直接看100pts解法,仍然枚举一次行的全排列,复杂度是$O(C_m^c)$,这个时候我们得到的其实是一行数列,数列上的每个数有选择和不选择两种状态,选择每个数会增加分值,分别是:

 - 自身的分数(该列上下数字差值绝对值和)
 
 - 相对左侧(右侧)的分数,左侧和右侧的选择都会影响这种相对分数
 
 那么提前预处理出数列上每个数两两之间相对的分数和自身的分数,因为要枚举$m$的原因,所以复杂度是$O(n^3)$
 
 进行dp,设$f[i][l]$为已选择到数列上第$i$个数,已选择$l$个数的最大值,很明显$f[i][l]=min(f[k][l-1]+sum[k][i])$ 其中$k \subseteq[1,i-1]$,不需要太多优化,只要枚举$i,k,l$就可以在$O(n^3)$之内解决问题,该种方法总复杂度$O(C_m^c n^3)$可以通过$n=16$的数据


```
//2019/7/19->Riko->AtNCU->luoguP2258
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

bool digit (int ch) { return (ch <= ‘9‘ and ch >= ‘0‘);}
inline int in () {
    int x = 0, ch = getchar(), base = 1;
    while (!digit(ch)) {
        if (base == ‘-‘) base = -1;
        ch = getchar();
    }
    while (digit(ch)) x = x*10+ch-‘0‘, ch = getchar();
    return x*base;
}
template <typename T> inline void smin (T& x, T y) { if (x > y) x = y;}
inline int abs (int x) { return (x < 0) ? -x : x;}

const int N = 24;
int n, m, r, c, ans, cnt;
int f[N][N], a[N][N], sum[N][N], suml[N], bol[N];

void work () {
    memset(sum, 0, sizeof(sum));
    memset(suml, 0, sizeof(suml));
    memset(f, 0x3f, sizeof(f));
    for (int i = 1; i <= n; ++i) {
        for (int j = i+1; j <= n; ++j) {
            int seg = 0;
            for (int l = 1; l <= m; ++l) {
                if (bol[l]) seg += abs(a[i][l]-a[j][l]);
            }
            sum[i][j] = sum[j][i] = seg;
        }
        int last = 0;
        for (int l = 1; l <= m; ++l) {
            if (bol[l]) {
                if (last) suml[i] += abs(a[i][l]-last);
                last = a[i][l];
            }
        }
    }
    for (int i = 1; i <= n; ++i) { f[i][1] = suml[i];}
    for (int i = 1; i <= n; ++i) {
        for (int l = 2; l <= r; ++l) {
            int Min = f[0][0];
            for (int j = 1; j < i; ++j) {
                smin(Min, f[j][l-1]+sum[j][i]);
            }
            f[i][l] = Min+suml[i];
        }
    }
    for (int i = 1; i <= n; ++i) { smin(ans, f[i][r]);}
}
void dfs (int idx, int num) {
    if (num == c) {
        work();
        return;
    }
    for (int i = idx; i <= m; ++i) {
        bol[i] = true;
        dfs(i+1, num+1);
        bol[i] = false;
    }
}
void prepare () {
    n = in(); m = in();
    r = in(); c = in();
    ans = 999999999;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            a[i][j] = in();
        }
    }
    dfs(1, 0);
    printf("%d", ans);
} int main () { prepare();}
```

【题解】 P2258 子矩阵

标签:pre   pac   题解   const   tchar   min   template   需要   string   

原文地址:https://www.cnblogs.com/NHDR233/p/11246712.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!