凸包上的DP,WQS二分优化

本文介绍了如何使用WQS二分优化解决凸包上的动态规划问题,详细讲解了适用范围、证明过程,并通过例题演示帮助理解。文章探讨了在整数坐标情况下二分k值的可能性,以及处理多个最大值的情况。最后,提供了练习题目供读者巩固学习。

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

前言

考炸了CSP,调整调整心态。。。

然后就学了这个地步。

感觉学较为简单的算法真的能够愉悦身心。

参考文献

个题题解

题集:https://www.cnblogs.com/HocRiser/p/9834069.html

关于整点证明的启发虽然我没看懂:https://www.cnblogs.com/CreeperLKF/p/9045491.html

关于最小度限制生成树与WQS二分的区别:https://www.luogu.com.cn/blog/EndSaH/solution-cf125e

例题演示

例题

题目链接

中文题意,自己看吧。。。

从题解出发的讲解

如果你没有思路的话,我可以很负责任的告诉你。

刚开始我也没思路,就连题解需要的前提条件都是我已经证明了题解做法正确性之后才推出的前提条件。

我采用的是克鲁斯卡尔的做法。

我们思考一下,我们要让生成树包含一定量的白边,那么就要改变白边在生成树中的位置,那么我们可以给每条边加上一个权值 k k k(可以为正,可以为负)。

当然这里需要证明一个东西,就是增加的数字越小,白边选中的也就越多,也许你会说是废话,但是有时候这些东西还是要证明的,感性理解太吃shit了。

首先,对于前面的一条为未选中的白边, A − > B A->B A>B,那么在生成树中肯定有 A − > B A->B A>B的一条链。

在这里插入图片描述

我们对于其中权值最大的黑边而言(箭头指的边,如果没有的话,那么这个白边不可能被选中),白边的值原本大于黑边,当加完权值后大于最大的黑边的话,那么这条黑边将彻底淘汰,也就是多了一条白边。

可以发现,这个权值 k k k是整数就够了,不用是小数,如果 k k k 1 1 1 1 1 1产生了大量白边怎么办呢?后面会讲。

那么我们现在就是要证 k − 1 k-1 k1的生成树比 k k k的生成树的白边要多或者相同。

那么也就是对于 k k k的生成树中,更多的白边取代了黑边,而原本的白边,也不会消失,为什么,因为不可能有白边超过他呀。

同时对于 k k k越大而言呢,你可以视作其实是黑边的加上 − k -k k,然后证明黑边数量递增就行了。

所以就是正确的了,然后跑完以后只要把生成树的代价减去白边的数量就行了。

那么其实我们就可以通过二分 k k k值来控制白边的数量。

但是其实还涉及一个问题,什么问题呢?

就是如果有相同权值的白边和黑边,选什么。

那么我们只要在排序优先白边,然后问大于等于就行了。

不过需要注意一个事情,就是什么呢吗,就是白边虽然选的多了,但是其实我们只是来统计数量的,我们最终只选 n e e d need need个白边,多出的就当选了黑边了,所以每次减去也是 n e e d ∗ k need*k needk

当然,你也可以用这个来证明一个事情,就是选不同数量的白边都可以在对应的 k k k成为最小值。

那么就可以上代码了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define  N  51000
#define  M  110000
using  namespace  std;
int  fa[N];
int  findfa(int  x)
{
   
	if(fa[x]==x)return  x;
	return  (fa[x]=findfa(fa[x]));
}
struct  node
{
   
	int  x,y,c,type;
}a[M];int  len,dep/*白边的系数*/,need;
inline  void  ins(int  x,int  y,int  c,int  type){
   len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].type=type;}
inline  bool  cmp(node  x,node  y){
   return  (x.c+x.type*dep)==(y.c+y.type*dep)?x.type>y.type:(x.c+x.type*dep)<(y.c+y.type*dep);}
int  n,m,ans,zans;
int  check()
{
   
	sort(a+1,a+m+1,cmp);
	for(int  i=1;i<=n;i++)fa[i]=i;
	int  cnt=0,bai=0;ans=0;
	for(int  i=1;i<=m;i++)
	{
   
		int  x=a[i].x,y=a[i].y;
		int  tx=findfa(x),ty=findfa(y);
		if(tx!=ty)
		{
   
			fa[tx]=ty;
			cnt++;ans+=a[i].c;
			if(a[i].type)bai++,ans+=dep;
			if(cnt==n-1)return  bai;
		}
	}
}
int  main()
{
   
//	freopen("tree7.in","r",stdin);
	scanf("%d%d%d",&n,&m,&need);
	for(int  i=1;i<=m;i++)
	{
   
		int  x,y,c,type;scanf("%d%d%d%d",
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值