「一本通 1.3 例 5」weight 题解(玄学搜索对象)
题目描述:
已知前1.2.3....n项的前缀和 和 后缀和,记为st[],但是已经打乱,已知n个数在集合S中,求原数列。当存在多组可能的数列时,求字典序最小的数列。
题目思路:
真的玄学搜索对象,玄学时间复杂度。
暴力算法:
因为S中的数字1<=a[i]<=500,枚举每个数字a[i]。
时间复杂度500^1000.
优化算法:
题目中有很多限制条件,我们可以发现
sum1[k]+sum2[k+1]=all
sum1[k+1]-sum1[k]=a[i]
sum2[k+1]-sum2[k]=a[i]
而sum1[1],sum2[1]必定是最小的。
我们对于st[ ]排序,枚举每个st[i]放的位置。
它能放到前面或者后面。
前缀和或者后缀和之差在a[i]中出现过,就搜索。
可以省略很多限制条件。
理论时间复杂度:O(2^1000)
代码:
#include<bits/stdc++.h> #define ll long long #define R register using namespace std; const int N=2000005; int n,st[N],m,a[N],ton[N],flag,ans[N],all; inline void dfs(R int l,R int r,R int now,R int presum,R int sufsum) { if(flag)return; if(l==r) { R int w3=all-presum-sufsum; if(ton[w3]) { ans[l]=w3; flag=1; } return; } R int w1=st[now]-presum; if(w1<=500&&w1>=1&&ton[w1])//属于前缀 { ans[l]=w1; dfs(l+1,r,now+1,st[now],sufsum); } if(flag)return; R int w2=st[now]-sufsum; if(w2<=500&&w2>=1&&ton[w2])//属于后缀 { ans[r]=w2; dfs(l,r-1,now+1,presum,st[now]); } } int main(){ scanf("%d",&n); for(R int i=1;i<=2*n;i++)//前后缀和 scanf("%d",&st[i]); scanf("%d",&m); for(R int i=1;i<=m;i++){ scanf("%d",&a[i]); ton[a[i]]=1; } sort(st+1,st+2*n+1); all=st[2*n]; dfs(1,n,1,0,0); for(R int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }