UVA-1151 Buy or Build

思路是参照紫书第十一章例题11-3,很不错的一道题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int MAXN=1000+10;//点数
const int MAXM=1000000+10;//边数
int UF[MAXN];
struct Edge
{
	int u,v,w;
}edge[MAXM],ne[MAXN];
int tol,nt; //初始化为0 
void addedge(int u,int v,int w)
{
	edge[tol].u=u;
	edge[tol].v=v;
	edge[tol++].w=w;
} 
void addedge_n(int u,int v,int w)
{
	ne[nt].u=u;
	ne[nt].v=v;
	ne[nt++].w=w;
} 
bool cmp(Edge a,Edge b) 
{
	return a.w<b.w; 
}
int find(int x) 
{  
	if(UF[x]==-1) return x;  
	else return UF[x]=find(UF[x]);
}
int Kruskal(int n)
{
	int cnt=0;
	int ans=0;
	for(int i=0;i<nt;i++)
	{
		int u=ne[i].u;   
		int v=ne[i].v;   
		int w=ne[i].w; 
		int t1=find(u);
		int t2=find(v);
		if(t1!=t2)
		{
			UF[t1]=t2;
			ans+=w;
			cnt++;
		}
		if(cnt==n-1) break;
	}
	if(cnt<n-1) return -1;
	return ans;
}
int x[MAXN],y[MAXN];
vector<int> s[8];
int cost[8],num[8];
bool vis[8];
int n,q,T,a;
int ans;
void dfs(int d,int c)
{
	if(d==q)
	{
		int t=n,k;
		memset(UF,-1,sizeof(UF));
		for(int i=0;i<q;i++) //先把套餐里的点分别加入并查集 
		{
			k=-1;
			if(vis[i])
			for(int j=0;j<num[i];j++)
			{
				if(k!=-1)
				{
					int t1=find(s[i][j]);
					int t2=find(k);
					if(t1!=t2)
					{
						UF[t1]=t2;
						t--;
					}
				}
				else k=s[i][j]; //第一个点作为根 
			}
		}
		ans=min(ans,c+Kruskal(t)); //计算最后结果 
		return;
	}
	dfs(d+1,c);
	vis[d]=true;
	dfs(d+1,c+cost[d]);
	vis[d]=false;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&q);
		for(int i=0;i<q;i++)
		{
			s[i].clear();
			scanf("%d%d",&num[i],&cost[i]);
			for(int j=0;j<num[i];j++) //保存套餐 
			{
				scanf("%d",&a);
				s[i].push_back(a);
			}
		}
		tol=nt=0;
		for(int i=0;i<n;i++)
		{
			scanf("%d%d",&x[i],&y[i]);
			for(int j=0;j<i;j++)
			{
				a=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
				addedge(i+1,j+1,a); //加边 
			}
		}
		sort(edge,edge+tol,cmp);
		memset(UF,-1,sizeof(UF));
		int cnt=0;
		ans=0;
		for(int i=0;i<tol;i++)
		{
			int u=edge[i].u;   
			int v=edge[i].v;   
			int w=edge[i].w; 
			int t1=find(u);
			int t2=find(v);
			if(t1!=t2)
			{
				UF[t1]=t2;
				addedge_n(u,v,w); //先找出n-1条最小生成树的边,作为新的边集 
				ans+=w;
			}
			if(cnt==n-1) break;
		}
		memset(vis,false,sizeof(vis));
		dfs(0,0); //枚举套餐 
		printf("%d\n",ans);
		if(T) printf("\n");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值