其他分享
首页 > 其他分享> > CF EDU 105 C - 1D Sokoban

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