uva1151
题意:被题目和图给吓到了,直接上这一题很简单。就是让所有城市都连通,有两种方案,一种是两个点之间修一条边,价格为两点距离的平方。另一种方案是买一个套餐,套餐里的城市都连通。求最下的花费。
题解:二进制状态压缩,枚举套餐,然后再并查集找最小生成树。和HDU1102很像,已经构造的边就合并,让后再查找剩下的。一次AC,很舒服~~
代码:
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
typedef pair<int,int>pii;
int const N = 1000 + 10;
int const M = 1000000;
int const inf = 0x7f7f7f7f;
int n,m,q,cost[10],fa[N];
vector<int>G[10];
pii a[N];
struct Edge
{
int u,v,w;
}p[M];
bool cmp(Edge a,Edge b){
return a.w < b.w;
}
int Distance(int x,int y){
return (a[x].first-a[y].first)*(a[x].first-a[y].first)+(a[x].second-a[y].second)*(a[x].second-a[y].second);
}
void Init(){
for(int i=0;i<10;i++) G[i].clear();
scanf("%d%d",&n,&q);
for(int i=0;i<q;i++){ //记录套餐
int cnt;
scanf("%d%d",&cnt,&cost[i]); //cost记录套餐的价格
for(int j=0;j<cnt;j++){
int tmp;
scanf("%d",&tmp);
G[i].push_back(tmp); //记录套餐的城市
}
}
for(int i=1;i<=n;i++){ //记录坐标
int x,y;
scanf("%d%d",&x,&y);
a[i] = make_pair(x,y);
}
m = 0;
for(int i=1;i<=n;i++) //记录任意两点之间的记录
for(int j=i+1;j<=n;j++)
p[++m] = (Edge){i,j,Distance(i,j)};
}
int find(int x){
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
void Union(int x,int y){
int fx = find(x), fy = find(y);
if(fx != fy) fa[fx] = fy;
}
void updata(int k){ //加入第k个套餐
for(int i=0;i<G[k].size();i++)
for(int j=i+1;j<G[k].size();j++)
Union(G[k][i],G[k][j]);
}
int Kruskal(){
int sum = 0;
for(int i=1;i<=m;i++){
int fx = find(p[i].u), fy = find(p[i].v);
if(fx != fy){
fa[fx] = fy;
sum += p[i].w;
}
}
return sum;
}
int main(){
int T;
scanf("%d",&T);
bool flag = false;
while(T--){
if(flag){
printf("\n");
flag = false;
}
Init();
int ans = inf;
sort(p+1,p+1+m,cmp);
for(int i=0;i<(1<<q);i++){ //开始枚举套餐
int sum = 0;
for(int j=1;j<=n;j++) fa[j] = j;
for(int j=0;j<q;j++)
if(i&(1<<j)){
updata(j);
sum += cost[j]; //
} //选中第j个套餐
sum += Kruskal();
ans = min(ans,sum);
}
printf("%d\n",ans);
flag = true;
}
}