题意:给你一个n,和2*n个数的序列,问能否由两个长度为n的数组构造出这个序列,构造过程为每次从两个数组中取排在第一个数中小的那个数。
solution:将原序列分块,比如一个序列1、4、2、5、3,1为一个块,4和2为一个块,5和3位一个块,最后跑一遍0-1背包看这些块能否恰好拼成n
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a[20005];
ll w[20005],f[20005],c[20005],inf=-9999999999999999;
int main()
{
ll t,n;
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
for(int i=1;i<=2*n;i++)
{
scanf("%lld",&a[i]);}
queue<ll> q;
int x=1;
while(x<=2*n)
{
if(x==2*n)
{
q.push(1);break;}
for(int y=x+1;y<=2*n;y++)
{
if(a[y]>a[x])
{
q.push(y-x);x=y;break;}
if(y==2*n)
{
q.push(y+1-x);x=y+1;break;}
}
}
int cnt=0;
while(!q.empty())
{
ll u=q.front();
q.pop();
c[++cnt]=u;
}
for(int i=1;i<=cnt;i++)
{
w[i]=1;}
for(int i=1;i<=n;i++)
{
f[i]=inf;}
f[0]=0;
for(int i=1;i<=cnt;i++)
{
for(int j=n;j>=c[i];j--)
{
f[j]=max(f[j],f[j-c[i]]+w[i]);
}
}
if(f[n]>0)
{
printf("YES\n");}
else
{
printf("NO\n");}
}
return 0;
}