题目大意:给出n个点的坐标,连边的费用为两端点的欧几里得距离。给出q种套餐,购买某种套餐花费Ci,该套餐中所有点将连通,求最小花费。
思路:读完题后知道是kruskal最短路问题,但如果枚举所有套餐,又要给所有边排序,又要kruskal,规模太大肯定超时。
所以需要优化:先kruskal求一次得到n-1条边,然后从这n-1条边中用二进制枚举子集的方法枚举套餐。
附上AC代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1000+5;
typedef long long ll;
int T;//T组样例
int n, q;//共n个点,q个套餐
int city_num[8], pack_spend[8];//city_num[]:单个套餐内的点数目 pack_spend[]:套餐花费
int pack_city[8][maxn];//pack_city[]:单个套餐内点的集合
int x[maxn], y[maxn];//坐标
int par[maxn];//根节点
int m, cnt;
struct edges {
int fr, to, val;
bool operator <(const edges & e)
{
return val < e.val;
}
}_edge[maxn*maxn],edge[maxn];//
//初始化
void init()
{
for (int i = 0; i <= n; i++)par[i] = i;
}
//找根节点
int find(int x)
{
return x == par[x] ? x : par[x] = find(par[x]);
}
//计算欧几里得距离
int dis(int a, int b)
{
return (x[a] - x[b])*(x[a] - x[b]) + (y[a] - y[b])*(y[a] - y[b]);
}
//kruskal
ll kruskal()
{
ll ans = 0;
for (int i = 1; i < n; i++)
{
int dx = find(edge[i].fr);
int dy = find(edge[i].to);
if (dx != dy)
{
par[dx] = dy;
ans += edge[i].val;
}
}
return ans;
}
int main()
{
//ios::sync_with_stdio(false);
scanf("%d",&T);
while(T--)
{
cnt = m = 0;
//read
scanf("%d %d", &n, &q);
for (int i = 0; i < q; i++)
{
scanf("%d%d", & city_num[i], & pack_spend[i]);
for (int j = 1; j <= city_num[i]; j++)
scanf("%d", & pack_city[i][j]);
}
for (int i = 1; i <= n; i++)
scanf("%d%d", &x[i], &y[i]);
//solve
init();
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
_edge[++cnt].fr = i;
_edge[cnt].to = j;
_edge[cnt].val = dis(i, j);
}
}
//全体点的kruskal
ll ans = 0;
sort(_edge + 1, _edge + cnt + 1);
for (int i = 1; i <= cnt; i++)
{
int dx = find(_edge[i].fr);
int dy = find(_edge[i].to);
if (dx != dy)
{
par[dx] = dy;
ans += _edge[i].val;
edge[++m] = _edge[i];
}
}
//套餐的二进制枚举
for (int t = 0; t < (1 << q); t++)
{
ll anxd = 0;
init();
for (int i = 0; i < q; i++)
{
if (t&(1 << i))
{
anxd += pack_spend[i];
for (int j = 2; j <= city_num[i]; j++)
par[find(pack_city[i][j-1])] = find(pack_city[i][j]);
}
}
anxd += kruskal();
ans = min(ans, anxd);
}
printf("%lld\n", ans);
if (T)printf("\n");
}
// system("pause");
return 0;
}