【貪心】硬幣遊戲
阿新 • • 發佈:2020-11-30
題意
一共有\(n\)個莊家。
你可以到莊家那邊下注,每次可以猜大猜小,猜一次需要一個單位的花費(可以都猜,也可以都不猜,都猜花費\(2\)個單位)。
可以到任意個莊家那裡下賭注。
開彩結果不是大就是小。
如果開彩結果是大,你就可以得到你之前猜大的莊家相應的\(a_i\)單位的收益。
如果開彩結果是小,你就可以得到你之前猜小的莊家相應的\(b_i\)單位的收益。
請你設定一個策略,使得你在最壞情況下的收益最大。
思路
最壞情況即取開小和開大中結果的最小值。
可以發現,因為可以都下注,就可以把\(a\)和\(b\)分開做。
實際上求得就是這個東西:\(max(min(\sum_{i \in S_1}a_i,\sum_{i \in S_2}b_i)-(c(S_1)+c(S_2)))\)
固定\(c(S_1)+c(S_2)\),則求\(min(\sum_{i \in S_1}a_i,\sum_{i \in S_2}b_i)\)最大。
如果列舉\(c(S_1)和c(S_2)\),肯定取最大的\(c(S_1)和c(S_2)\)個\(a_i和b_i\)做比較,然而複雜度是\(O(n^2)\)的。
不妨令小的一邊是\(a\),那麼隨著\(a\)增大,相應的\(b\)也應增大。用兩個指標維護即可。對於小的一邊為\(b\),也做一遍即可。此處也是取前若干個最大值,易證。
程式碼
#include <cstdio> #include <algorithm> int n; double ans; double a[100001], b[100001]; bool cmp(double x, double y) { return x > y; } int main() { freopen("coin.in", "r", stdin); freopen("coin.out", "w", stdout); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%lf %lf", &a[i], &b[i]); std::sort(a + 1, a + n + 1, cmp); std::sort(b + 1, b + n + 1, cmp); for (int i = 1; i <= n; i++) { a[i] += a[i - 1]; b[i] += b[i - 1]; } for (int i = 1, j = 1; i <= n && j <= n; i++) { for (; a[i] - i - j > b[j] - i - j && j <= n; j++) ; if (j <= n) ans = std::max(ans, a[i] - i - j); } for (int i = 1, j = 1; i <= n && j <= n; i++) { for (; b[i] - i - j > a[j] - i - j && j <= n; j++) ; if (j <= n) ans = std::max(ans, b[i] - i - j); } printf("%.4lf", ans); }