洛谷 P2123 皇后游戏
思路
感谢 liuzibujian 的思路
错误做法
感觉和国王游戏很像,所以像国王游戏一样,试着用相邻比较法试一下可不可以做。
设现在要比较的两个大臣为 \(i, j\),\(j\) 在 \(i\) 后,\(i\) 前面所有大臣左手的值的和为 \(x\),\(i\) 的前一个大臣的奖金数为 \(y\),如果不交换,那么到目前最大的奖金数 \(c_j\) 就是
\[\max(\max(x+a_i+ b_i, y+b_i), x+a_i+a_j)+b_j
\]
化简得
\[\max(x+a_i+b_i+b_j,y+b_i+b_j,x+a_i+a_j+b_j)
\]
同理,若交换,则得到的最大值为
\[\max(x+a_j+b_j+b_i,y+b_j+b_i,x+a_j+a_i+b_i)
\]
假设不交换更优,那么有
\[\max(x+a_i+b_i+b_j,y+b_i+b_j,x+a_i+a_j+b_j)\le\max(x+a_j+b_j+b_i,y+b_j+b_i,x+a_j+a_i+b_i)
\]
把 \(y+b_i+b_j\) 消去得到
\[\max(x+a_i+b_i+b_j,x+a_i+a_j+b_j)\le\max(x+a_j+b_j+b_i,x+a_j+a_i+b_i)
\]
现在就可以消去 \(x\),得到
\[\max(a_i+b_i+b_j,a_i+a_j+b_j)\le\max(a_j+b_j+b_i,a_j+a_i+b_i)
\]
化简得
\[\max(b_i,a_j)+a_i+b_j\le \max(b_j,a_i)+a_j+b_i
\]
移项得
\[\max(b_i,a_j)-a_j-b_i\le \max(b_j,a_i)-a_i-b_j
\]
可以看出,两边较大的值都被消掉了,得到的就是
\[-\min(a_j,b_i)\le - \min(b_j,a_i)
\]
进而得到
\[\min(b_j,a_i)\le\min(a_j,b_i)
\]
一个简单的式子,也就是排序所用的式子。
正确做法
其实上面的式子是不满足传递性的,所以要找一个满足传递性的式子,正确做法应该是:
- 当 \(a_i<b_i,a_j<b_j\) 时,\(a_i\le a_j\),按 \(a\) 升序排序
- 当 \(a_i=b_i,a_j=b_j\) 时,随便排
- 当 \(a_i>b_i,a_j>b_j\) 时,\(b_i\ge b_j\),按 \(b\) 降序排序
要保证 \(1\) 在 \(2\) 前,\(2\) 在 \(3\) 前,
令 \(d_i=\dfrac{a_i-b_i}{|a_i-b_i|}\),第一组的 \(d\) 为 \(-1\),第二组为 \(0\),第三组为 \(1\)
按 \(d\) 排序,如果 \(d\le 0\),按 \(a\) 升序排序,否则按 \(b\) 排序。
代码
错误做法
/*
Name: P2123 皇后游戏
Author: Loceaner
Date: 01/09/20 19:13
Description: 贪心
Debug:
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int A = 1e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, c[A], sum[A];
struct node { int a, b; } w[A];
bool cmp(node x, node y) {
return min(y.b, x.a) < min(y.a, x.b);
}
inline void solve() {
n = read();
for (int i = 1; i <= n; i++)
w[i].a = read(), w[i].b = read();
sort(w + 1, w + 1 + n, cmp);
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + w[i].a;
for (int i = 1; i <= n; i++)
c[i] = max(c[i - 1], sum[i]) + w[i].b;
cout << c[n] << '\n';
return;
}
signed main() {
int T = read();
while (T--) solve();
}
正确做法
/*
Name: P2123 皇后游戏
Author: Loceaner
Date: 01/09/20 19:13
Description: 贪心
Debug:
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int A = 1e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, c[A], sum[A];
struct node { int a, b, d; } w[A];
bool cmp(node x, node y) {
return x.d != y.d ? x.d < y.d : (x.d <= 0 ? x.a < y.a : x.b > y.b);
}
inline void solve() {
n = read();
for (int i = 1; i <= n; i++) {
int a = read(), b = read();
w[i].a = a, w[i].b = b;
w[i].d = (w[i].a - w[i].b > 0) ? 1 : -1;
}
sort(w + 1, w + 1 + n, cmp);
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + w[i].a;
for (int i = 1; i <= n; i++)
c[i] = max(c[i - 1], sum[i]) + w[i].b;
cout << c[n] << '\n';
return;
}
signed main() {
int T = read();
while (T--) solve();
}
转载不必联系作者,但请声明出处