题意
有一个正整数序列,长度为 n n n。现有一个长度为 2 × n 2\times n 2×n 的序列为其前缀后缀和,一个集合 S S S 中有正整数序列中的所有元素(不是全部包含),问现在如果要使这个正整数序列的字典序最小为多少?
思路
我们可以将集合 S S S 中的所有元素全部存入一个标记数组中,每次搜索时,如果当前搜到的元素在此数组当中,也就表示当前可已将其计入答案。可是不保证它的字典序最小,所以可以将记录其前缀后缀的序列从大到小排序。
对于当前数据范围,考虑爆搜,具体处理可以用前缀和和后缀和来实现。
我们需要两个指针来维护前缀和和后缀和。指针 l l l 维护从 1 1 1 到 l l l 的前缀和,指针 r r r 维护从 r r r 到 n n n 的后缀和。每次搜到当前前缀后缀和数组中的元素,减去当前维护的前缀和或后缀和,如果满足数据范围 [ 1 , 500 ] [1,500] [1,500],且在集合 s s s 当中,就说明当前合法且最优,因为我们已经排完序了。将其计入答案并接着搜。
最后还需设计一个出口。若两个指针重合了,说明已经搜到了前缀后缀和数组当中的最后一位。因为指针重合了,所以应该是当前下标元素满足前缀和与后缀和,需要将前缀后缀和数组中当前下标元素减去当前维护的前缀和和后缀和。并且如果符合数据范围且在集合 S S S 当中,就说明已经形成了一个合法的解,直接输出即可。、
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,x;
int s[1000001];
bool vis[1000001];
int ans[1000001];
void dfs(int u,int l,int r,int lsum,int rsum)
{
if(l==r)
{
int x=s[n<<1]-lsum-rsum;
if(x>=1&&x<=500&&vis[x])
{
ans[l]=x;
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
exit(0);
}
return ;
}
int x=s[u]-lsum;
if(x>=1&&x<=500&&vis[x])
{
ans[l]=x;
dfs(u+1,l+1,r,lsum+x,rsum);
}
int y=s[u]-rsum;
if(y>=1&&y<=500&&vis[y])
{
ans[r]=y;
dfs(u+1,l,r-1,lsum,rsum+y);
}
return ;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=(n<<1);i++)
scanf("%d",&s[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d",&x),vis[x]=1;
sort(s+1,s+(n<<1)+1);
dfs(1,1,n,0,0);
return 0;
}