Luogu P2460 [SDOI2007] 科比的比賽 題解

bluewindde發表於2024-09-07

題意:給定 \(n \times m\) 的矩陣 \(A = (a_{ij})\) 和長為 \(m\) 的數列,矩陣的元素在 \([0, 1]\) 上。在矩陣每一行選擇一個數,最大化選中的數的乘積的前提下最大化得分,但矩陣每一列至多選擇一個數。求最大乘積和對應的最大得分。

考慮狀壓 DP,設 \(dp_{i, j}\) 表示已經處理前 \(i\) 列,狀態集合為 \(j\) 的行已經選定時的最大乘積。

轉移

\[dp_{i, j} = \sum\limits_{k \in j} dp_{i - 1, j - \{k\}} \cdot a_{i, k} \]

邊界

\[dp_{i, 0} = 1 \]

答案

\[dp_{m, U} \]

其中 \(U\) 是全集。

最大得分容易與乘積同時轉移。

時間複雜度 \(O(nm 2^n)\),滾動陣列最佳化空間後可以透過 \(50\) 分。

考慮貪心,將矩陣每一行按 \(a_{i, j}\) 為第一關鍵字,\(s_j\) 為第二關鍵字降序排序,至多選擇前 \(n\) 大的位置。

所以排序後將每一行可能被選的位置提出來做一次 DP,至多有 \(O(n^2)\) 個可能被選的位置,時間複雜度最佳化為 \(O(nm \log m + n^3 \log n + n^3 2^n)\),可以透過。

#include <algorithm>
#include <iomanip>
#include <iostream>

using namespace std;

typedef long double ld;

const ld eps = 1e-10;

int n, m;
int w[100005];
int s[100005];

ld a[12][100005];
int b[12][100005];
int id[100005], len;
ld c[12][100005];

ld dp[2][1050];
int f[2][1050];

static inline void solve() {
    cin >> n >> m;
    int lim = 1 << n;
    for (int i = 1; i <= m; ++i)
        cin >> w[i];
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> a[i][j];
            b[i][j] = j;
        }
        sort(b[i] + 1, b[i] + m + 1,
             [i](const int x, const int y) {
                 return (abs(a[i][x] - a[i][y]) < eps)
                            ? (w[x] > w[y])
                            : (a[i][x] - a[i][y] > eps);
             });
        for (int j = 1; j <= n && j <= m; ++j)
            id[++len] = b[i][j];
    }
    sort(id + 1, id + len + 1);
    len = (int)(unique(id + 1, id + len + 1) - id - 1);
    for (int i = 1; i <= len; ++i) {
        for (int j = 1; j <= n; ++j)
            c[j][i] = a[j][id[i]];
        s[i] = w[id[i]];
    }
    dp[0][0] = 1;
    for (int i = 1; i <= len; ++i) {
        for (int j = 0; j < lim; ++j) {
            dp[i & 1][j] = dp[(i + 1) & 1][j];
            f[i & 1][j] = f[(i + 1) & 1][j];
            for (int k = 1; k <= n; ++k) {
                if (j & (1 << (k - 1))) {
                    ld val = dp[(i + 1) & 1][j ^ (1 << (k - 1))] * c[k][i];
                    if (val < eps)
                        continue;
                    if (val - dp[i & 1][j] > eps) {
                        dp[i & 1][j] = val;
                        f[i & 1][j] = f[(i + 1) & 1][j ^ (1 << (k - 1))] + s[i];
                    } else if (abs(val - dp[i & 1][j]) < eps)
                        f[i & 1][j] = max(f[i & 1][j], f[(i + 1) & 1][j ^ (1 << (k - 1))] + s[i]);
                }
            }
        }
    }
    cout << fixed << setprecision(12) << dp[len & 1][lim - 1] << endl;
    cout << f[len & 1][lim - 1] << endl;
}

signed main() {
#ifndef ONLINE_JUDGE
    freopen("P2460.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
    return 0;
}

相關文章