CF EDU 105 C - 1D Sokoban
作者:互联网
C - 1D Sokoban
二分 + 找性质
可分正负的箱子分别讨论
本题的关键是发现一个重要的性质:因为推箱子这个过程会让被推到的箱子成为连续的一段,若想让在特殊位置的箱子最多,则这一段的终点一定要在特殊位置上(起点也可以, 这里的一定不是说不在特殊位置就取不到最优,而是在特殊位置上的某些情况一定可以取到最优,所以只考虑在特殊位置上的最大值就是全局的最大值)
证明思路:若结尾不是在特殊位置上,那么那把它移到最近的特殊位置上一定不会更劣
所以可以枚举每一个特殊位置,让连续一段箱子的末尾被推到这个点上,看这时的答案是多少
答案 = 这一段箱子覆盖的特殊位置 + 这一段箱子之后的点中,有多少箱子原来就在特殊位置上
第一项可二分求出,第二项可用前缀和求出
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, m;
int a[N], b[N], c[N], d[N], pre[N];
map<int, bool> mp;
int solve(int a[], int b[], int n, int m)
{
if (n <= 0 || m <= 0)
return 0;
sort(a + 1, a + n + 1);
sort(b + 1, b + m + 1);
mp.clear();
for (int i = 1; i <= n; i++)
mp[a[i]] = true;
for (int i = 1; i <= m; i++)
{
pre[i] = pre[i-1];
if (mp.count(b[i]))
pre[i]++;
}
int ans = 0;
for (int i = 1; i <= m; i++)
{
int len = upper_bound(a + 1, a + n + 1, b[i]) - a - 1;
int r = b[i], l = r - len + 1;
int cnt = upper_bound(b + 1, b + m + 1, r) - lower_bound(b + 1, b + m + 1, l);
cnt += pre[m] - pre[i];
ans = max(ans, cnt);
}
return ans;
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--)
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++)
cin >> b[i];
int ans = 0;
int cnta = 0, cntb = 0;
//正数
for (int i = 1; i <= n; i++)
if (a[i] > 0) c[++cnta] = a[i];
for (int i = 1; i <= m; i++)
if (b[i] > 0) d[++cntb] = b[i];
ans = solve(c, d, cnta, cntb);
//负数
cnta = 0, cntb = 0;
for (int i = 1; i <= n; i++)
if (a[i] < 0) c[++cnta] = -a[i];
for (int i = 1; i <= m; i++)
if (b[i] < 0) d[++cntb] = -b[i];
ans += solve(c, d, cnta, cntb);
cout << ans << endl;
}
return 0;
}
标签:箱子,特殊,cntb,int,位置,1D,Sokoban,include,105 来源: https://www.cnblogs.com/hzy717zsy/p/16300772.html