【BZOJ 2395】Timeismoney

本文探讨了在给定无向连通图中寻找生成树的问题,目标是最小化边的权重乘积之和。文章提供了详细的算法实现,包括使用Kruskal算法进行求解,并通过比较不同情况下的权重乘积来找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


problem

给出有 n n n 个点, m m m 条边的无向连通图,每条边有两个参数 c c c t t t,现要求出一个生成树,使得边的 ∑ c × ∑ t \sum c\times \sum t c×t 最小,输出这种情况下的 ∑ c \sum c c ∑ t \sum t t(当有多个方案是输出 ∑ c \sum c c 更小的那个)。

数据范围: 1 ≤ n ≤ 200 1\le n\le200 1n200 1 ≤ m ≤ 10000 1\le m\le10000 1m10000 0 ≤ t , c ≤ 255 0\le t,c\le255 0t,c255


solution

最小乘积生成树的板子题啦。

想学习的话可以参考这篇博客,写的比较详细。


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N=205,M=10005;
int n,m,fa[N];
struct point{
	int x,y;
	point(){}
	point(int x,int y):x(x),y(y){}
	friend point operator+(const point &a,const point &b)  {return point(a.x+b.x,a.y+b.y);}
	friend point operator-(const point &a,const point &b)  {return point(a.x-b.x,a.y-b.y);}
	friend int dot(const point &a,const point &b)  {return a.x*b.x+a.y*b.y;}
	friend int cross(const point &a,const point &b)  {return a.x*b.y-a.y*b.x;}
	friend ll calc(const point &a)  {return (ll)a.x*a.y;}
};
struct edge{int u,v,c,t,w;}e[M];
point ans=point(inf,inf),Minc,Mint;
bool operator<(const edge &a,const edge &b)  {return a.w<b.w;}
int find(int x)  {return x==fa[x]?x:fa[x]=find(fa[x]);}
point Kruskal(){
	sort(e+1,e+m+1);
	point now=point(0,0);
	for(int i=1;i<=n;++i)  fa[i]=i;
	for(int i=1;i<=m;++i){
		int x=find(e[i].u),y=find(e[i].v);
		if(x!=y)  fa[x]=y,now=now+point(e[i].c,e[i].t);
	}
	if(calc(ans)>calc(now)||(calc(ans)==calc(now)&&ans.x>now.x))  ans=now;
	return now;
}
void solve(point A,point B){
	for(int i=1;i<=m;++i)
		e[i].w=e[i].c*(A.y-B.y)+e[i].t*(B.x-A.x);
	point C=Kruskal();
	if(cross(B-A,C-A)>=0)  return;
	solve(A,C),solve(C,B);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)  scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].c,&e[i].t),++e[i].u,++e[i].v;
	for(int i=1;i<=m;++i)  e[i].w=e[i].c;Minc=Kruskal();
	for(int i=1;i<=m;++i)  e[i].w=e[i].t;Mint=Kruskal();
	solve(Minc,Mint);
	printf("%d %d\n",ans.x,ans.y);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值