这年头怎么二分图最大匹配都能用贪心求了啊!
lizihan250 · · 题解
如果你做过最小路径覆盖问题的话,应该可以看出来这题可以建模为二分图匹配。具体的,对于每个数,拆成左部点和右部点。我们定义
显然直接跑二分图最大匹配是不行的,就算你使用一些技巧成功把边数压到了
这启示我们,应该考虑这张图的特殊性质。
我们注意到,
为什么这样一定不劣呢?假定原先有一个位置在
当我们把所有的
实现时考虑维护每个数出现的位置和其是否作为左部点(以及右部点)被匹配。每个上述序列至多被遍历两遍,且所有序列中元素数量的总和为
#include<bits/stdc++.h>
using namespace std;
const int Maxn=1000000;
int T;
int n,ans;
int nums[Maxn+10];
vector<vector<pair<int,bool> > > p1,p2;
void merge(int x,int y)
{
int ptr1=0,ptr2=0;
while(true)
{
while(ptr1<p1[x].size()&&!p1[x][ptr1].second) ptr1++;
if(ptr1==p1[x].size()) return;
while(ptr2<p2[y].size()&&(!p2[y][ptr2].second||p2[y][ptr2].first<p1[x][ptr1].first)) ptr2++;
if(ptr2==p2[y].size()) return;
ans--;
p1[x][ptr1].second=false;
p2[y][ptr2].second=false;
}
return;
}
void merge2(int x,int y)
{
int ptr1=0,ptr2=0;
stack<int> stk;
while(true)
{
while(ptr2<p2[x].size()&&!p2[x][ptr2].second) ptr2++;
if(ptr2==p2[x].size()) return;
while(ptr1<p1[y].size()&&p1[y][ptr1].first<p2[x][ptr2].first)
{
if(p1[y][ptr1].second) stk.push(ptr1);
ptr1++;
}
if(!stk.empty())
{
ans--;
int res=stk.top();
stk.pop();
p2[x][ptr2].second=false;
p1[y][res].second=false;
}
ptr2++;
}
return;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>T;
while(T--)
{
cin>>n;
p1.clear(),p2.clear();
p1.resize(2*n+5),p2.resize(2*n+5);
for(int i=1;i<=n;i++)
{
cin>>nums[i];
p1[nums[i]].push_back({i,true});
p2[nums[i]].push_back({i,true});
}
ans=n;
for(int i=1;i<2*n;i++)
{
merge(i,i+1);
merge2(i,i+1);
}
cout<<ans<<"\n";
}
return 0;
}