最優集合 51Nod - 1821
阿新 • • 發佈:2018-11-23
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 */