一个集合S的优美值定义为:最大的x,满足对于任意i∈1,x1,x,都存在一个S的子集S',使得S'中元素之和为i。
给定n个集合,对于每一次询问,指定一个集合S1和一个集合S2,以及一个数k,要求选择一个S2的子集S3(|S3|<=k),使得S1∪S3的优美值最大。
(集合元素可以重复)
Input第一行一个数n,(n<=1000)
接下来n行,每行描述一个集合:
第一个数m,表示集合大小,接下来m个数,表示集合中的元素(m<=1000,元素<=10^9)
第n+2行一个数T,表示询问次数(T<=10000)
接下来T行,每行3个数a,b,k,表示指定第a个集合为S1,第b个集合为S2,k的意义如题(a<=n,b<=n,k<=100,000)OutputT行,每行一个数,表示对应询问所能达到的最大优美值Sample Input
2 6 1 2 3 8 15 32 6 1 1 1 1 1 1 1 1 2 3
Sample Output
64
题意:给出N个数列。Q次询问,每次询问形如(a,b,k),表示选定第a个数列,然后在b数列里选择k个数,使得ans最大,满足1到ans都可以被表示为这些选择的数的和的形式。
思路:先看子问题:给定一个数列,如何求最大的ans; 假设已经选出一些数,ans=[1,x],如现在给出一个数t<=x+1,那么ans=[1,x+t],负责ans不变。
所以现在即是排序,求出每个集合是ans,然后考虑在b数列选择最大的数t<=ans+1,更新ans=ans+t;同时,a数列的数可以随便选。
#include<bits/stdc++.h> using namespace std; const int maxn=1010; int a[maxn][maxn],ans[maxn],pos[maxn]; void read(int &x){ x=0; char c=getchar(); while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar(); } int main() { int N,Q,x,y,k,i,j; read(N); for(i=1;i<=N;i++){ read(a[i][0]); for(j=1;j<=a[i][0];j++) read(a[i][j]); } for(i=1;i<=N;i++){ sort(a[i]+1,a[i]+a[i][0]+1); for(j=1;j<=a[i][0];j++){ if(a[i][j]<=ans[i]+1) pos[i]=j, ans[i]+=a[i][j]; else break; } } scanf("%d",&Q); while(Q--){ read(x); read(y); read(k); int res=ans[x]; priority_queue<int>q; int np=upper_bound(a[y]+1,a[y]+a[y][0]+1,res+1)-a[y],tk=k; for(i=np-1;i>=1&&tk;i--){ q.push(a[y][i]); tk--; } //先挑大的选,这样k后面几个就可以不加进来。 是个小小的优化。 i=np; int tp=pos[x]; while(k--&&!q.empty()){ res+=q.top();q.pop(); while(tp+1<=a[x][0]&&a[x][tp+1]<=res+1) tp++,res+=a[x][tp]; for(;i<=a[y][0];i++) if(a[y][i]<=res+1) q.push(a[y][i]); else break; } printf("%d\n",res); } return 0; }