E. The Contest

题目

题意:

    1到n,一共n个元素。k1个元素在集合1,k2个元素在集合2,k3个元素在集合3。每次操作可以令一个元素从原来的集合到任意的集合。要求使得集合1中的数最小,集合3中的数最大,剩下的数在集合2中的最小操作次数。

分析:

    先考虑暴力,枚举集合1的最大值和集合3的最小值,求代价暴力算。复杂度O(n^3)
    考虑优化,集合1的最大值确定时,集合3的最小值在暴力转移,从i->i+1,如果i原来是集合2的,那么代价就较于原先少了1,如果原来是集合3的,那么代价就多了1。所以我们只要选出这段区间种类2减去种类3最大的那个下标,就是集合3的最小值。
    那么如何找这个最小值呢,我们只要维护一个后缀最大值就可以了,这个值v[i]是从1-i这段区间种类2减去种类3的值。在找[i…n]这个区间的最大值后,减去v[i]即可,这个值就是省下来的花费。枚举时再维护一下[1,i]全部放在集合1的cost1和[i+1,n]全部放在集合3的cost2,减去节约下来的花费。这样就可以计算出答案了。

#include <iostream>
using namespace std;

int kind[200005],v[200005],maxn[200005];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int k1,k2,k3;
	cin >> k1 >> k2 >> k3;
	int n = k1 + k2 + k3;
	int x;
	for (int i = 1; i <= k1; i++)
	{
		cin >> x;
		kind[x] = 1;
	}
	for (int i = 1; i <= k2; i++)
	{
		cin >> x;
		kind[x] = 2;
	}
	for (int i = 1; i <= k3; i++)
	{
		cin >> x;
		kind[x] = 3;
	}
	int val = 0;
	for (int i = 1; i <= n; i++)
	{
		if( kind[i] == 2 ) val ++;
		else if( kind[i] == 3 ) val --;
		v[i] = val; 
	}
	for (int i = n; i >= 1; i--)
	{
		if( i == n ) maxn[i] = v[i];
		else maxn[i] = max(maxn[i+1],v[i]);
	}
	int cost1 = 0,cost2 = 0;
	for (int i = 1; i <= n; i++)
	{
		if( kind[i] == 1 || kind[i] == 2 ) cost2 ++;
	}
	int ans = cost2 - max(0,maxn[1]);
	for (int i = 1; i <= n; i++)
	{
		if( kind[i] == 1 )
		{
			cost2 --;
		}else if( kind[i] == 2 )
		{
			cost1 ++;
			cost2 --;
		}else
		{
			cost1 ++;
		}
		if( i == n )
		{
			ans = min(ans,cost1);
			continue;
		}
		int t = cost2 - (maxn[i] - v[i]);
		ans = min(ans,cost1+t);
		//if( ans == 2 ) cout << "z " << i << '\n';
	}
	cout << ans << '\n';
	return 0;
}

发布了103 篇原创文章 · 获赞 6 · 访问量 7017

猜你喜欢

转载自blog.csdn.net/weixin_44316314/article/details/104759550