1. 程式人生 > >最優集合 51Nod - 1821

最優集合 51Nod - 1821

https://www.51nod.com/Challenge/Problem.html#!#problemId=1821

假設已選元素之和是sum 下一個待選的數是val 只有當val<=sum+1時 才滿足選擇當前元素之後[1,sum+val]內任意數都可以被已選元素表示 如果val>sum+1 則[sum+1,val-1]都無法用已選元素表示

鬼谷子錢袋問題為什麼可以用二進位制 就是因為二進位制是滿足上述條件的所有方法中最優的

具體實現就是掃集合a 當a[i]<=sum+1時就加上接著走 不然就在b集合裡找[1,sum+1]內最大的未被選的數 並查集維護一下即可

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;

ll val[maxn][maxn];
int len[maxn],f[maxn];
int n,q;

int getf(int p)
{
    if(f[p]==p) return p;
    else return f[p]=getf(f[p]);
}

void unite(int u,int v)
{
    int fu,fv;
    fu=getf(u),fv=getf(v);
    if(fu!=fv) f[fv]=fu;
}

template <class T>
inline void _cin(T &ret)
{
    char c;
    ret=0;
    while((c=getchar())<'0'||c>'9');
    while(c>='0'&&c<='9'){
        ret=ret*10+(c-'0');
        c=getchar();
    }
}

int main()
{
    ll sum;
    int i,j,a,b,k,l,r,m,p;
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //scanf("%d",&n);
    _cin(n);
    for(i=1;i<=n;i++){
        //scanf("%d",&len[i]);
        _cin(len[i]);
        for(j=1;j<=len[i];j++){
            //scanf("%lld",&val[i][j]);
            _cin(val[i][j]);
        }
        sort(val[i]+1,val[i]+len[i]+1);
    }
    //scanf("%d",&q);
    _cin(q);
    while(q--){
        //scanf("%d%d%d",&a,&b,&k);
        _cin(a),_cin(b),_cin(k);
        k=min(k,len[b]);
        for(i=0;i<=len[b];i++) f[i]=i;
        sum=0,j=0;
        for(i=1;i<=len[a];i++){
            if(val[a][i]<=sum+1) sum+=val[a][i];
            else{
                if(k==0) break;
                k--;
                while(j+1<=len[b]&&val[b][j+1]<=sum+1) j++;
                p=getf(f[j]);
                if(p==0) break;
                sum+=val[b][p];
                unite(p-1,p);
                i--;
            }
        }
        if(i==len[a]+1){
            while(k>0){
                k--;
                while(j+1<=len[b]&&val[b][j+1]<=sum+1) j++;
                p=getf(f[j]);
                if(p==0) break;
                sum+=val[b][p];
                unite(p-1,p);
            }
        }
        printf("%lld\n",sum);
    }
    return 0;
}

/*
2
6 1 3 4 4 7 8
5 1 2 3 4 5
10
1 2 3
*/