CodeForces - 95E: Lucky Country (多重背包)
pro:给定N个点,M条边,现在你要给一些连通块加边,使得至少存在一个连通块的大小是由4和7组成的数字。问至少加边数量。
sol: 看似一个很难的题目。 首先不要想太难了,还是应该想能不能用背包做。 我们把块的大小相同的分到一组,就可以分组背包了。 然后注意到组别的大小其实不会太大,因为1*1+2*2+3*3+4*4+x*x<=1e5; x是1e2级别的。 所以做100次多重背包。 总的复杂度也才O(100*N*K),K是个比较小的常数,真正的复杂度小于这个;
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; int fa[maxn],num[maxn],sum[maxn]; int dp[maxn],N; int find(int x) { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } void pack(int v,int now) { for(int i=N;i>=v;i--){ dp[i]=min(dp[i-v]+now,dp[i]); } } void solve(int v,int x) { int now=1; while(now<x){ pack(v*now,now); x-=now; now*=2; } if(x) pack(v*x,x); } bool check(int x){ int tx=x; while(x){ int t=x%10; if(t!=4&&t!=7) return false; x/=10; } return true; } int main() { int M,v,u,fu,fv; scanf("%d%d",&N,&M); rep(i,1,N) num[i]=1,fa[i]=i; rep(i,1,M){ scanf("%d%d",&u,&v); fu=find(u); fv=find(v); if(fu!=fv) num[fu]+=num[fv],fa[fv]=fu; } rep(i,1,N) { fu=find(i); if(fu==i) sum[num[fu]]++; } rep(i,1,N) dp[i]=N+M+1; rep(i,1,N) if(sum[i]) solve(i,sum[i]); int res=N+M+1; rep(i,1,N) if(check(i)) res=min(dp[i],res); if(res==N+M+1) puts("-1"); else printf("%d\n",res-1); return 0; }
It is your time to fight!