poj2784 Buy or Build 最小生成樹

題目大意:給定n個城市(1<=n<=1000),c個聯通塊(0<=c<=8)。再給定c個連通塊的信息:包含cnt個點和價格cost,並給出cnt個點的編號。再給定n個城市的座標。現在要將n個城市連在一起,可以購買聯通塊也可以新建邊(代價是兩城市距離的平方)。求最小代價。

c較小,可以二進制枚舉需要添加那些聯通塊,然後每次kruskal更新答案

#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;
const int N = 1010;
const int M = 1e6+10;
struct Point
{
    int x, y;
    int getval(const Point &p)const
    {
        return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y);
    }
}point[N];
struct Edge
{
    int u, v, w;
    Edge(){}
    Edge(int _u, int _v)
    {
        u = _u;
        v = _v;
        w = point[_u].getval(point[_v]);
    }
    bool operator <(const Edge &e)const
    {
        return w<e.w;
    }
}edge[M];
int tot;
int fa[N];
int n, c;
vector <int> g[10];
int cost[10];

void init_fa()
{
    for(int i = 1; i <= n; ++i) fa[i] = i;
}

int findf(int x)
{
    return x==fa[x]?x:fa[x]=findf(fa[x]);
}

bool merge_(int x, int y)
{
    int f1 = findf(x);
    int f2 = findf(y);
    if(f1==f2) return 0;
    fa[f1] = f2;
    return 1;
}

int kruskal()
{
    int ret = 0;
    int cnt = 0;
    for(int i = 1; i <= tot; ++i)
    {
        if(merge_(edge[i].u, edge[i].v))
        {
            ++cnt;
            ret += edge[i].w;
        }
        if(cnt==n-1) break;
    }
    return ret;
}

int solve()
{
    init_fa();
    int ret = kruskal();
    for(int i = 0; i<(1<<c); ++i)
    {
        init_fa();
        int sum = 0;
        for(int j = 0; j < c; ++j)
        {
            if(!((i>>j)&1)) continue;
            sum += cost[j];
            for(int k = 1; k < g[j].size(); ++k)
                merge_(g[j][0], g[j][k]);
        }
        ret = min(ret, sum+kruskal());
    }
    return ret;
}

int main()
{
    scanf("%d %d", &n, &c);
    for(int i = 0; i < c; ++i)
    {
        int cnt;
        scanf("%d %d", &cnt, &cost[i]);
        for(int j = 1; j <= cnt; ++j)
        {
            int x;
            scanf("%d", &x);
            g[i].push_back(x);
        }
    }
    for(int i = 1; i <= n; ++i)
        scanf("%d %d", &point[i].x, &point[i].y);
    tot = 0;
    for(int i = 1; i <= n; ++i)
        for(int j = i+1; j <= n; ++j)
            edge[++tot] = Edge(i, j);
    sort(edge+1, edge+tot+1);
    printf("%d\n", solve());
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章