平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方。另外还有q(0<=q<=8)个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci。
kruskal:
先求一次原图的最小生成树,得到n-1条边,然后每次枚举完套餐后只考虑套餐中的边和这n-1条边,则枚举套餐之后再求最小生成树。
精髓:kruskal算法中,那些两端已经属于同一个连通分量的边不会再加到生成树里面。
那么买了套餐后,相当于一些边的权变为0,而对于不在套餐中的每条边e,排序在e之前的边一个也没少,反而可能多了一些权值为0的边。
所以在 原图kruskal时被扔掉的边,在购买套餐后的Kruskal中也一样会被扔掉
二进制枚举子集
for(int i=0;i<(1<<n);++i){
for(int j=0;j<n;++j){
if(i&(1<<j)) printf("%d ",j);
}
puts(" ");
}
WA的代码,找不出错误,标记一下
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define FORD(i,a,b) for(int i=(a);i<=(b);++i)
#define mes(s,c) memset(s,c,sizeof(s))
const int maxn=1010;
const int INF=1<<30;
using namespace std;
int MST_w;
int n,m;
int f[maxn];
struct Edge{
int u,v;
int w;
Edge(int from,int to,int d):u(from),v(to),w(d){}
bool operator<(const Edge&x)const{
return w<x.w;
}
};
vector<Edge> edges;
struct val{
int node[maxn];
int cnt;
int w;
}c[10];
vector<pair<int,int> >node;
void makeSet(){REP(i,n+1)f[i]=i;}
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
int MST[maxn];
void kruskal()
{
sort(edges.begin(),edges.end());
makeSet();int cnt=0;
MST_w=0;
REP(i,edges.size()){
int f_u=find(edges[i].u);
int f_v=find(edges[i].v);
if(f_u!=f_v){
f[f_u]=f_v;
MST[cnt++]=i;
MST_w+=edges[i].w;
if(cnt==n-1) break;
}
}
// cout<<"最小生成树=="<<MST_w<<endl;
}
void _kruskal(int &w)
{
REP(i,n-1){
int f_u=find(edges[MST[i]].u);
int f_v=find(edges[MST[i]].v);
if(f_u!=f_v){
w+=edges[MST[i]].w;
f[f_u]=f_v;
}
}
}
void solve()
{
int ans=MST_w;
REP(i,1<<m){
makeSet();
int w=0;
REP(j,m){
if(i&(1<<j)){//枚举子集
w+=c[j].w;
REP(k,c[j].cnt-1){//将套餐中的点连通
int f_u=c[j].node[k];
int f_v=c[j].node[k+1];
if(f_u!=f_v) f[f_u]=f_v;
}
}
}//最小生成树
_kruskal(w);
ans=min(ans,w);
}
printf("%d\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.cpp","r",stdin);
#endif // ONLINE_JUDGE
scanf("%d%d",&n,&m);
node.clear();edges.clear();
REP(i,m){scanf("%d%d",&c[i].cnt,&c[i].w);REP(j,c[i].cnt)scanf("%d",&c[i].node[j]);}//输入套餐
REP(i,n){//输入点坐标
int x,y;
scanf("%d%d",&x,&y);
node.push_back(make_pair(x,y));
}
REP(i,n)FOR(j,i+1,n){//预处理边权
int x=int(node[i].first-node[j].first);
int y=int(node[i].second-node[j].second);
int w=x*x+y*y;
edges.push_back(Edge(i+1,j+1,w));
}
kruskal();//求原图的MST,记录边的序号
solve();
return 0;
}