• Buy or Build(最小生成树+状压枚举)


    题意:

    n个城市,告诉每个城市的坐标,还有q个联通块,现在要把这n个城市连起来,可以购买联通块(每个有一定的费用),或者新建一条边(费用为点之间的距离的平方),问最小费用是多少。

    思路:

    首先可以想到朴素的做法,二进制枚举每个连通块选还是不选,判断该状态下图是否已经联通,如果未联通的话再从朴素的边里选择,时间复杂度为(O(2^{q}*n^{2}+n^{2}*log_{n}))
    考虑怎么优化,要理解kruskal的算法本质,就是按照边权排序后先选择小的边,那么我们用连通块去替换已选的边时,只会替换那些原本就存在最小生成树中的边,这样才能保证最优解。
    也就是说,我们先对原图做一遍kruskal,并且记录出所有要选择的边的集合记作(edge1),然后我们二进制枚举连通块,判断该状态下图是否联通,如果未联通的话就从(edge1)里加边,这样就完成了很大的剪枝。时间复杂度为(O(2^{q}*(n-1)+n^{2}*log_{n}))

    代码:

    
    const int maxn=1e6+7;
    
    struct node{
    	ll u,v,w;
    };
    node edge1[500500],edge[500500];
    struct node1{
    	ll m,w;
    	vector<int>v;
    }a[10];
    int root[1100],n,q;
    PLL pos[1100];
    
    bool cmp(node a,node b){
    	return a.w<b.w;
    }
    
    int Find(int x){
    	if(x!=root[x]) root[x]=Find(root[x]);
    	return root[x];
    }
    
    void init(int n){
    	for(int i=1;i<=n;i++) root[i]=i;
    }
    
    ll dis(PLL a,PLL b){
    	return (a.first-b.first)*(a.first-b.first)+(a.second-b.second)*(a.second-b.second);
    }
    
    void Union(vector<int>v,ll &tot){
    	for(int i=0;i<v.size();i++){
    		for(int j=i+1;j<v.size();j++){
    			int fu=Find(v[i]),fv=Find(v[j]);
    			if(fu!=fv) root[fu]=fv,tot++;
    		}
    	}
    }
    
    void solve(){
        n=read,q=read;
        for(int i=0;i<q;i++) a[i].v.clear();
        for(int i=0;i<q;i++){
        	a[i].m=read,a[i].w=read;
        	for(int j=1;j<=a[i].m;j++){
        		int x=read;
        		a[i].v.push_back(x);
        	}
        }
        for(int i=1;i<=n;i++) pos[i].first=read,pos[i].second=read;
        ll idx=0;
        for(int i=1;i<=n;i++){
        	for(int j=i+1;j<=n;j++){
        		edge[++idx]={i,j,dis(pos[i],pos[j])};
        	}
        }
        sort(edge+1,edge+1+idx,cmp);
        init(n);
        ll cnt=0,ans=0;
        for(int i=1;i<=idx;i++){
        	int u=edge[i].u,v=edge[i].v;
        	int fu=Find(u),fv=Find(v);
        	if(fu!=fv){
        		cnt++;
        		root[fu]=fv;
        		edge1[cnt]={u,v,edge[i].w};
        		ans+=edge[i].w;
        	}
        	if(cnt==n-1) break;
        }
        for(int i=0;i<(1<<q);i++){
        	ll res=0,tot=0;
        	init(n);
        	for(int j=0;j<q;j++)
        		if((i>>j)&1){
        			res+=a[j].w;
        			Union(a[j].v,tot);
        		}
        	if(tot==n-1){
        		ans=min(ans,res);
        	}
        	else{
        		for(int j=1;j<=n-1;j++){
        			int u=edge1[j].u,tv=edge1[j].v,w=edge1[j].w;
        			int fu=Find(u),fv=Find(tv);
        			if(fu!=fv){
        				tot++;
        				root[fu]=fv;
        				res+=w;
        			}
        			if(tot==n-1) break;
        		}
        		ans=min(ans,res);
        	}
        }
        printf("%lld
    ",ans);
    }
    
    int main() {
    	int T=read;
    	while(T--){
    		solve();
    		if(T) puts("");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    C#Windows服务程序安装常见问题解决方法
    解决access 导出 excel 字段截断错误的问题
    MySQL创建方法错误:This function has none of DETERMINISTIC, NO SQL
    解决问题 “You don't have permission to access /index.html on this server.”
    无法枚举容器中的对象,访问被拒绝的解决方法
    php xml操作
    php 字符串截取函数
    PHP iconv 解决utf-8和gb2312编码转换问题
    IIS6,IIS7中查看w3wp进程
    Solaris设备管理
  • 原文地址:https://www.cnblogs.com/OvOq/p/14802253.html
Copyright © 2020-2023  润新知