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
*/