题意:给定 \(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_JUDGEfreopen("P2460.in", "r", stdin);
#endifios::sync_with_stdio(false);cin.tie(0);cout.tie(0);solve();return 0;
}