Luo Gu solution to a problem UVA1151 [to buy or build Buy or Build]

[Title] Italy

There on the plane \ (n (n <= 1000 ) \) points, your task is to get all n points Unicom. To do this, you can create some of the side, the cost is equal to the two end points of the Euclidean distance squared . Another \ (q (q <= 8 ) \) a package can be purchased, if you buy the first \ (I \) a package, the package in all nodes will become connected to each other. The first \ (i \) a package of spending is \ (C_i \) .

【algorithm】

\(Kruskal\)

【analysis】

Most likely to think of the algorithm is: first enumeration buy what package, the package contains the right value is set to \ (0 \) , then the minimum spanning tree. Since the amount of enumeration \ (O (2 ^ Q) \) , to the edge of the time complexity is sorted \ (O (n-2logn ^) \) , and after sorting each \ (Kruskal \) time complexity of the algorithm is \ (O (^ n-2) \) , so the total time complexity is \ (O (Qn ^ 2 + 2 ^ ^ n-2logn) \) , for the size of the subject is too large.

Just a small optimized to reduce the time complexity: first find a picture (without any later package) of a minimum spanning tree, to give \ (n-1 \) sides, the remaining sides useless. What then enumerate buy packages (where you can use a compressed state ideology), the enumerator when the package after minimum spanning tree, on the edge of the map have been very few.

Why can this happen? Most explanations are not proven. Here is the proof

First look at \ (kruskal \) algorithm, which side do not enter the minimum spanning tree. The answer is: both ends of the side already belong to the same collection. After buying the package, some of side edges corresponding to contingency become \ (0 \) , and for each edge of the package is not \ (E \) , sorting \ (E \) side before a no less , but also some more weight is \ (0 \) side, so the original \ (kruskal \) is "thrown away" at the edge, in the back of the enumeration in the same package will be thrown away.

[Code]

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000+10;
const int MAXM=MAXN*MAXN;
int n,q,T,ans=0x3f3f3f3f;
int s[10][MAXN];
int c[10];
struct Node2
{
    int x,y;
}city[MAXN];
struct Node
{
    int u,v,w;
}edge[MAXM],g[MAXM];
int cnt,m;
int fa[MAXN];
int save[MAXN];
inline int read()
{
    int tot=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        tot=tot*10+c-'0';
        c=getchar();
    }
    return tot;
}
inline bool cmp(Node x,Node y)
{
    return x.w<y.w;
}
inline int find(int k)
{
    if(fa[k]==k)return k;
    else return fa[k]=find(fa[k]);
}
inline int init_kruskal()
{
    int tot=0,cc=0;
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=cnt;i++)
    {
        if(fa[find(edge[i].u)]!=fa[find(edge[i].v)])
        {
            fa[find(edge[i].u)]=find(edge[i].v);
            tot++;
            cc+=edge[i].w;
            save[tot]=i;//记录边
        }
        if(tot==n-1)break;
    }
    return cc;
}
inline int kruskal(int tot)
{
    int cc=0,t=tot;
    for(int i=1;i<n;i++)
    {
        if(find(g[i].u)!=find(g[i].v))
        {
            fa[find(g[i].u)]=find(g[i].v);
            t++;
            cc+=g[i].w;
        }
        if(t==n-1)break;
    }
    return cc;
}
inline void solve()
{
    for(int ss=0;ss<(1<<q);ss++)//状压思想,用二进制来表示选还是不选
    {
        for(int i=1;i<=n;i++)
        fa[i]=i;//初始化并查集
        int tot=0;//选中套餐中被连接的点数
        int cc=0;//套餐的钱
        for(int k=1;k<=q;k++)
        {
            if(ss&(1<<(k-1)))//如该套餐被选中
            {
                //cout<<k<<" ";
                cc+=c[k];
                for(int i=1;i<=s[k][0];i++)
                {
                    for(int j=i+1;j<=s[k][0];j++)
                    {
                        //cout<<s[k][0]<<" "<<k<<" "<<s[k][i]<<" "<<s[k][j]<<endl;
                        if(find(s[k][i])!=find(s[k][j]))
                        {
                            fa[find(s[k][i])]=find(s[k][j]);
                            tot++;
                        }
                    }
                }
            }
        }
        //cout<<endl;
        //cout<<cc<<endl;
        //cout<<tot<<" "<<kruskal(tot)<<" "<<cc<<endl;
        ans=min(ans,kruskal(tot)+cc);//更新最小值
    }
}
int main()
{
    T=read();
    while(T--)
    {
        cnt=0;
        n=read();q=read();
        for(int i=1;i<=q;i++)
        {
            s[i][0]=read();c[i]=read();
            for(int j=1;j<=s[i][0];j++)
                s[i][j]=read();//读入套餐
        }
        for(int i=1;i<=n;i++)
            city[i].x=read(),city[i].y=read();
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                edge[++cnt].u=i;
                edge[cnt].v=j;
                edge[cnt].w=(city[i].x-city[j].x)*(city[i].x-city[j].x)+(city[i].y-city[j].y)*(city[i].y-city[j].y);
            }
        }
        sort(edge+1,edge+1+cnt,cmp);
        ans=init_kruskal();//原始图的最小生成树
        //cout<<ans<<endl;
        for(int i=1;i<n;i++)
        {
            g[i].u=edge[save[i]].u;
            g[i].v=edge[save[i]].v;
            g[i].w=edge[save[i]].w;
        }//建一个新图
        /*for(int i=1;i<n;i++)
        {
            cout<<g[i].u<<" "<<g[i].v<<" "<<g[i].w<<endl;
        }
        cout<<endl;*/
        solve();//准备枚举
        cout<<ans<<endl;
        if(T)cout<<endl;//UVA不会省略最后的换行符
    }
    return 0;
}

Guess you like

Origin www.cnblogs.com/hulean/p/11123030.html