Description
平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方。
另外还有q(0<=q<=8)个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci。
求最小花费。
Solution
对于套餐可以用子集枚举处理,求最小生成树时只需考虑原图是最小生成树中的边。
正确性可以按Kruskal过程,以前被舍弃的边选了套餐后依然会被舍弃。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 const int maxn=1005; 7 8 int x[maxn],y[maxn],p[maxn]; 9 int find(int x){return p[x]==x?x:p[x]=find(p[x]);} 10 struct edge{ 11 int u,v,w; 12 bool operator<(const edge&a) 13 const {return w<a.w;} 14 }_e[maxn*maxn],e[maxn]; 15 int dist(int a,int b){ 16 return (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]); 17 } 18 int q[8][maxn],c[8],t[8]; 19 int n,m,r,cnt; 20 21 void clear(){ 22 m=cnt=0; 23 } 24 25 ll solve(){ 26 ll ret=0; 27 for(int i=1;i<n;i++){ 28 int x=find(e[i].u),y=find(e[i].v); 29 if(x!=y){ 30 ret+=e[i].w; 31 p[x]=y; 32 } 33 } 34 return ret; 35 } 36 37 int main(){ 38 int T; 39 scanf("%d",&T); 40 41 while(T--){ 42 clear(); 43 scanf("%d%d",&n,&r); 44 for(int i=0;i<r;i++){ 45 scanf("%d%d",&t[i],&c[i]); 46 for(int j=1;j<=t[i];j++) 47 scanf("%d",&q[i][j]); 48 } 49 50 for(int i=1;i<=n;i++) 51 scanf("%d%d",&x[i],&y[i]),p[i]=i; 52 53 for(int i=1;i<=n;i++) 54 for(int j=i+1;j<=n;j++) 55 _e[++m]=(edge){i,j,dist(i,j)}; 56 sort(_e+1,_e+m+1); 57 58 ll ans=0; 59 for(int i=1;i<=m;i++){ 60 int x=find(_e[i].u),y=find(_e[i].v); 61 if(x!=y){ 62 e[++cnt]=_e[i]; 63 ans+=_e[i].w; 64 p[x]=y; 65 } 66 } 67 68 for(int S=0;S<(1<<r);S++){ 69 ll ansx=0; 70 for(int i=1;i<=n;i++) p[i]=i; 71 72 for(int i=0;i<r;i++) 73 if(S&(1<<i)){ 74 ansx+=c[i]; 75 for(int j=2;j<=t[i];j++) 76 p[find(q[i][j-1])]=find(q[i][j]); 77 } 78 ansx+=solve(); 79 ans=min(ans,ansx); 80 } 81 printf("%lld ",ans); 82 if(T) printf(" "); 83 } 84 return 0; 85 }