51Nod - 1821:最优集合 (求第一个不能被表示为多个数的和的数)(不错的动脑题)

一个集合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;
} 

猜你喜欢

转载自www.cnblogs.com/hua-dong/p/9125779.html
今日推荐