題意:給定 \(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;
}