2022昆明-Find the Maximum-(思维+贪心or二分01分数规划)

F

题意:
就是给你一个树,然后你需要选一些点集,这些点集任何两点只有一个简单路径,说白了就是条链,问你选的点集权值总和/点的个数的最大值或者最少值是多少。

思考:
当时我扫了一眼这题,一看那个公式感觉很复杂就跳过了,主要是过的人也很少,不过交的人挺多,害感觉题目的过题量和交题量太影响自己对题目的判断了。我感觉过的少,应该就没那么简单,看到平均值最大最小,立刻想到了,二分平均值mid,然后01规划树上dp去求最大值和最小值,但是wa掉了。如果eps开的更大一点,又TLE,最后到结束没调出来。我麻了,真折磨啊,这场K也卡精度,这个也卡。然后这个题就是求平均值最大,其实不要想那么复杂啊,如果能选单独一个点的话肯定就选最大的那个或者最小的就行了,这样肯定能保证平均值是最值,以前都在cf上做过,问你平均值最大是多少,不就是选一个或者两个嘛。但是对于这道题,仅仅选两个还是不够的,因为有可能选3个更好,因为这个是拿的一条链嘛,中间的中转点必须也要拿。所以dfs维护maxn1,maxn2,minn1,minn2。枚举拿3个还是2个去更新最大的ans就行了。
md,害,cf最近做少了,思维转化不过来,一直在写算法题,但是区域赛这样玩也没有算法题给你写啊,绝绝。以后要转化好做题方向。思维转化要快一点。

代码:

贪心做法:
int T,n,m,k;
db va[N];
db ans;

vector<int > e[N];

void dfs(int now,int p)
{
	db minn1 = inf,minn2 = inf;
	db maxn1 = -inf,maxn2 = -inf;
	for(auto spot:e[now])
	{
		if(spot==p) continue;
		dfs(spot,now);
		if(minn1>=va[spot])
		{
			minn2 = minn1;
			minn1 = va[spot];
		}
		else minn2 = min(minn2,va[spot]);
		if(maxn1<=va[spot])
		{
			maxn2 = maxn1;
			maxn1 = va[spot];
		}
		else maxn2 = max(maxn2,va[spot]);
		ans = max(ans,fabs(va[now]+va[spot])/2.0);
	}
	if(minn1!=inf&&minn2!=inf)
	{
		ans = max(ans,fabs(va[now]+minn1+minn2)/3.0);
	}
	if(maxn1!=inf&&maxn2!=-inf)
	{
		ans = max(ans,fabs(va[now]+maxn1+maxn2)/3.0);
	}
}

signed main()
{
	IOS;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>va[i];
	for(int i=1;i<n;i++)
	{
		int a,b;
		cin>>a>>b;
		e[a].pb(b);
		e[b].pb(a);
	}
	dfs(1,0);
	db anw = ans*ans/4.0;
	printf("%.6lf",anw);
	return 0;
}

01分数规划(TLE)#include<bits/stdc++.h>
#define int long long
#define db double
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;

const int N = 2e5+5;
const int inf = 1e18;

int n;
db va[N];
db dp[N];
db maxn = -inf;

vector<int > e[N];

void dfs(int now,int p,db mid)
{
	dp[now] = va[now]-mid;
	for(auto spot:e[now])
	{
		if(spot==p) continue;
		dfs(spot,now,mid);
		maxn = max(maxn,dp[now]+dp[spot]);
		dp[now] = max(dp[now],va[now]-mid+dp[spot]);
	}
	if(dp[now]!=va[now]-mid) maxn = max(maxn,dp[now]);
	
}

bool check(db mid)
{
	maxn = -inf;
	dfs(1,0,mid);
	if(maxn>=0) return true;
	return false;
}

void dfs1(int now,int p,db mid)
{
	dp[now] = va[now]-mid;
	for(auto spot:e[now])
	{
		if(spot==p) continue;
		dfs1(spot,now,mid);
		maxn = min(maxn,dp[now]+dp[spot]);
		dp[now] = min(dp[now],va[now]-mid+dp[spot]);
	}
	if(dp[now]!=va[now]-mid) maxn = min(maxn,dp[now]);
	
}

bool check1(db mid)
{
	maxn = inf;
	dfs1(1,0,mid);
	if(maxn<=0) return true;
	return false;
}

signed main()
{
	IOS;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>va[i];
	for(int i=1;i<n;i++)
	{
		int a,b;
		cin>>a>>b;
		e[a].pb(b);
		e[b].pb(a);
	}
	db l = 0,r = 1e6;
	while(r-l>1e-9)
	{
		db mid = (l+r)/2;
		if(check(mid)) l = mid;
		else r = mid;
	}
	db anw = 0;
	db ans = l*l/4.0;
	anw = max(anw,ans);
	l = -1e6,r = 0;
	while(r-l>1e-9)
	{
		db mid = (l+r)/2;
		if(check1(mid)) r = mid;
		else l = mid;
	}
	ans = l*l/4.0;
	anw = max(anw,ans);
	printf("%.6lf",anw);
	return 0;
}

总结:
以后多做思维题和构造题和数学题把,算法这个东西,区域赛不像前几年了,现在都是数学场,害,真绝。加油加油。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值