题意:
告诉你n 个点的坐标,你要在两个点之间连线,使得点全部相同,连边的费用为这两个点的欧几里得距离,你的目的是使这个费用最低,并且你有q(q <= 8) 个套餐可以用,一个套餐中会告诉你一些点已经连通, 问最少花费?
思路:
全部的点相通,很明显是最小生成树。
最容易想到的是,暴力枚举哪一个套餐用,哪一个套餐不用,在求最小生成树,这样会超时,因为原图是一个完全图,有100W个边。
有个小优化:
我们可以先求一边最小生成树,n-1个边,在n-1个边中在暴力枚举套餐。 这样边少了很多很多。
暴力枚举套餐,可以用二进制枚举子集的方法。
#include <cstdio>#include <cstring>#include <algorithm>#include <vector>#include <cmath>#include <cstdlib>#define Siz(x) (int)x.size()#define Max(a,b)((a)>(b)?(a):(b))#define Min(a,b)((a)>(b)?(b):(a))#define ps push_back#define sqr(x)((x)*(x))using namespace std;const int maxn = 1000 + 7;const int inf = 0x3f3f3f3f;typedef long long LL;int T, n, q, num_edge;vector<int>c[10];int w[10],fa[maxn];//double dist[maxn][maxn];struct Node{ int x, y; void read(){ scanf("%d %d",&x, &y); }}nod[maxn];struct edge{ int f,t; int d; int flag; bool Operator < (const edge& rhs) const { return d < rhs.d; }}p[maxn*maxn];void addedge(int x,int y,int d){ p[num_edge].f = x; p[num_edge].t = y; p[num_edge].flag = 0; p[num_edge++].d = d;}void DIST(){ num_edge = 0; for (int i = 1; i <= n; ++i){ for (int j = i+1; j <= n; ++j){ int t = (sqr(nod[j].y-nod[i].y) + sqr(nod[j].x-nod[i].x)); addedge(i,j,t); } }}int find(int x){ return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}void add(int x,int y){ int fx = find(x); int fy = find(y); if (fx != fy){ fa[fx] = fy; }}bool cmp(const edge& lhs, const edge& rhs){ return lhs.flag > rhs.flag;}int solve(int bin){ if (bin == n-1) return 0; int ans = 0; sort(p,p+num_edge); for (int i = 0; i < num_edge; ++i){ int u = p[i].f; int v = p[i].t; if (find(u) != find(v)){ add(u,v); ans += p[i].d; bin++; if (bin == n-1)break; } } return ans;}int main(){ scanf("%d",&T); int ks = 0; while(T--){ scanf("%d %d",&n, &q); LL ans = 0; for (int i = 0; i < 10; ++i)c[i].clear(); for (int i = 1; i <= q; ++i){ int x,y;scanf("%d",&x); scanf("%d",w+i); for (int j = 0; j < x; ++j) { scanf("%d",&y); c[i].ps(y); } } for (int i = 1; i <= n; ++i){ nod[i].read(); } if (ks++)puts(""); DIST(); sort(p,p+num_edge); int bin = 0; for (int i = 1; i <= n; ++i)fa[i] = i; for (int i = 0; i < num_edge; ++i){ int u = p[i].f; int v = p[i].t; if (find(u) != find(v)){ add(u,v); ans += p[i].d; p[i].flag = 1; bin++;// PRintf(" == %d %d/n",u,v); if (bin == n-1)break; } else p[i].flag = 0; } sort(p,p+num_edge,cmp); num_edge = n-1; for (int i = 0; i < (1<<q); ++i){ int tmp = i; LL sum = 0; int bin2 = 0; for (int j = 1; j <= n; ++j)fa[j] = j; for (int j = 0; j < q; ++j){ int id = q-j; if (tmp & 1){ for (int k = 1; k < Siz(c[id]); ++k){ if (find(c[id][k-1]) != find(c[id][k])){ addedge(c[id][k-1],c[id][k],inf); add(c[id][k-1],c[id][k]); bin2++; } } sum += w[id]; } tmp/=2; } sum += solve(bin2); ans = Min(ans,sum); num_edge = n-1; } printf("%lld/n",ans); } return 0;}/**17 32 4 1 23 3 3 6 73 9 2 4 50 24 02 04 21 30 54 4ans = 17**/
新闻热点
疑难解答