题意:
幸运数是只由 4 或 7 构成的数, n n n 个顶点组成若干个连通块,求至少需要加入几条边可以构造出一个大小是幸运数的连通块
题解:
先求出各个连通块大小,然后用 d p ( i ) dp(i) dp(i) 表示构成大小为 i i i 的连通块需要至少 d p ( i ) dp(i) dp(i) 条边,用二进制优化多重背包复杂度为 O ( n ∗ s q r t ( n ) ∗ l o g ( k ) ) O(n*sqrt(n)*log(k)) O(n∗sqrt(n)∗log(k))
struct DSU{
int n;
vector<int>f,siz;
DSU(int n):n(n),f(n+1),siz(n+1,1){iota(f.begin(),f.end(),0);}
int leader(int x){ return x==f[x]?x:f[x]=leader(f[x]); }
bool same(int x,int y){ return leader(x)==leader(y); }
int size(int x){ return siz[x]; }
bool merge(int x,int y){
x=leader(x),y=leader(y);
if(x==y) return false;
f[x]=y,siz[y]+=siz[x];
return true;
}
};
void problem_solver() {
int n,m; cin>>n>>m;
DSU d(n);
for(int i=0;i<m;i++){
int u,v; cin>>u>>v;
d.merge(u,v);
}
vector<int>dp(n+1,INF);
dp[0]=0;
vector<int>cnt(n+1),vis(n+1);
for(int i=1;i<=n;i++)
if(!vis[d.leader(i)]) cnt[d.size(d.leader(i))]++,vis[d.leader(i)]=1;
for(int i=1;i<=n;i++){
for(int j=1;cnt[i];j*=2){
int p=min(j,cnt[i]);
cnt[i]-=p;
int w=p*i;
for(int k=n;k>=w;k--)
dp[k]=min(dp[k],dp[k-w]+p);
}
}
auto check=[&](int x){
while(x){
if(x%10!=4&&x%10!=7) return 0;
x/=10;
}
return 1;
};
int ans=INF;
for(int i=1;i<=n;i++) if(check(i)) ans=min(ans,dp[i]);
cout<<(ans==INF?-1:ans-1)<<'\n';
}