JZOJ 3301. 【集训队互测2013】家族【枚举】


题目:

传送门


题意:

给出一张有很多条边的图,可能是非连通图
求在保留部分边的情况下同时符合喜好总和大于 k 、 k、 k频宽最小


分析:

我一开始想用逆推法,就是枚举 m m m个边,从 t a ta ta开始建边,随后向两边拓展,用 d f s dfs dfs实现。当然,只有愉快的 45 45 45
后来实在无望,就打了个暴力,忽然发现时间复杂度居然优秀的达到了 O ( m 2 ) O(m^2) O(m2)
暴力为枚举首边,然后不断往后加入其它边,直到喜好程度符合要求


代码:

#pragma GCC optimize(2)
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define LZX ILU
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}
LL n=read(),m=read(),k=read();
struct node{
	LL x,y,w;
}e[5005];
bool cmp(node x,node y) {return x.w<y.w;}
LL f[1005],like[1005];
LL find(LL i) {return f[i]==i?i:f[i]=find(f[i]);}
LL l,r,ans=99999999;
LL tf[1005][1005][2];
void mogui(LL c)
{
	if(c>=k) {ans=min(ans,e[r-1].w-e[l+1].w);return;}
	LL a=e[l].x,b=e[l].y;
	if(l>0) 
	{
		int co[1005];
		tf[l][r][0]=1;
		LL c1=0,c2=0;
		for(LL i=1;i<=n;i++)
		{
			if(find(i)==find(a)) c1++;
			if(find(i)==find(b)) c2++;
		}
		for(LL i=1;i<=n;i++) co[i]=f[i];
		f[find(a)]=find(b);
		l--;
		while(l>0&&(tf[l][r][0]||find(e[l].x)==find(e[l].y))) l--;
		mogui(c-like[c1]-like[c2]+like[c1+c2]);
		l++;
		for(LL i=1;i<=n;i++) f[i]=co[i];
	}	
	a=e[r].x,b=e[r].y;
	if(r<=m) 
	{
		int co[1005];
		tf[l][r][1]=1;
		LL c1=0,c2=0;
		for(LL i=1;i<=n;i++)
		{
			if(find(i)==find(a)) c1++;
			if(find(i)==find(b)) c2++;
		}
		for(LL i=1;i<=n;i++) co[i]=f[i];
		f[find(a)]=find(b);
		r++;
		while(r<=m&&(tf[l][r][1]||find(e[r].x)==find(e[r].y))) r++;
		mogui(c-like[c1]-like[c2]+like[c1+c2]);
		r--;
		for(LL i=1;i<=n;i++) f[i]=co[i];
	}
	return;
}
int main()
{
	for(LL i=1;i<=n;i++) like[i]=read();
	for(LL i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].w=read();
	sort(e+1,e+1+m,cmp);
	for(LL i=1;i<=m;i++)
	{
		memset(tf,0,sizeof(tf));
		l=i-1,r=i+1;
		for(LL j=1;j<=n;j++) f[j]=j;
		f[e[i].x]=find(e[i].y);
		mogui(like[2]+(n-2)*like[1]);
	}
	if(ans==99999999) printf("T_T");
	else cout<<ans;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值