题目
题意:
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;
}