題目鏈接:E. The Contest
題意:
三個人,每個人有一些數字,組合起來是一個排列,現在給你三個人手中數字的數量以及手中是哪幾個數,每次操作可以把一個人手中的數字給另個人,問你最少多少次操作使得第一個人擁有這個排列的某個前綴,第三個人擁有這個排列的某個後綴,第二個人擁有中間剩下的部分(允許有人沒有數字)。
思路:
- 看到三個人操作,我們先看兩個人操作時的情況:
假設到最後,第一個人擁有1 ~ i,第二個人擁有i+1 ~ n,那麼最小操作數爲第二個人1~i中擁有的數字加上第一個人i+1 ~ n中擁有的數字。我們可以採用前綴和,cnt1[k]表示第一個人前k個數中擁有的個數,cnt2[k]表示第二個人前k個數中擁有的個數,則表達式爲:
cnt2[i]+cnt1[n]−cnt1[i] - 受到啓發我們看三個人操作時的情況:
假設到最後,第一個人擁有1 ~ i,第二個人擁有i+1 ~ j,第三個人擁有j+1 ~ n,那麼最小操作數爲第二個人和第三個人1 ~ i中擁有的個數加上第一個人和第三個人i+1 ~ j中擁有的個數加上第一個人和第二個人j+1 ~ n中擁有的個數。我們可以採用前綴和,cnt1[k]表示第一個人前k個數中擁有的個數,cnt2[k]表示第二個人前k個數中擁有的個數,cnt3[k]表示第三個人前k個數中擁有的個數字表達式爲:
cnt2[i]+cnt3[i]+cnt1[j]−cnt1[i]+cnt3[j]−cnt3[i]+cnt1[n]−cnt1[j]+cnt2[n]−cnt2[j] - 化簡得到:
cnt2[i]−cnt1[i]+cnt3[j]−cnt2[j]+cnt1[n]+cnt2[n] - 我們從0~n枚舉i,接下來我們考慮j的取值,我們可以看到對於固定的i,只需要找到一個j使得該式子最小即可,那麼我們可以設置一個後綴minn[]數組,minn[i]表示當i≤j≤n時,cnt3[j]−cnt2[j]最小的值,那麼答案即爲:
cnt2[i]−cnt1[i]+minn[i]+cnt1[n]+cnt2[n]
參考博客:CODEFORCES 1257E - THE CONTEST
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 200010;
int n, m, k;
int num1[N];
int num2[N];
int num3[N];
int mini[N];
int a[N], b[N], c[N];
int main()
{
scanf("%d %d %d", &n, &m, &k);
int tot = n + m + k;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
num1[a[i]]++;
}
for(int i = 1; i <= m; i++)
{
scanf("%d", &b[i]);
num2[b[i]]++;
}
for(int i = 1; i <= k; i++)
{
scanf("%d", &c[i]);
num3[c[i]]++;
}
for(int i = 1; i <= tot; i++)
{
num1[i] += num1[i - 1];
num2[i] += num2[i - 1];
num3[i] += num3[i - 1];
}
mini[tot + 1] = inf;
for(int i = tot; i >= 0; i--)
{
int now = num3[i] - num2[i];
mini[i] = min(mini[i + 1], now);
}
int ans = inf;
for(int i = 0; i <= tot; i++)
{
int now = num2[i] - num1[i] + num1[tot] + num2[tot] + mini[i];
ans = min(ans, now);
}
printf("%d\n", ans);
return 0;
}