[BZOJ4206]最大团(计算几何+dp)

本文介绍了一种解决非二分图最大团问题的方法。通过巧妙地将问题转化为选择无相离或包含关系的线段问题,利用最长上升子序列算法进行求解,最终实现了时间复杂度为O(n²logn)的有效解决方案。

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

题目描述

传送门

题解

非二分图的最大团问题是npc hard(我不会告诉你我刚知道)
所以今天gang了半天是毫无意义的(逃

一个非常奇妙的转化
首先圆内和圆上的点扔掉
对于每一个点做它的两条切线,每个点的两个切点在圆上会形成一段连续的弧,两个点不能同时选,当且仅当两个弧相离或包含
栗子:
这里写图片描述
这里写图片描述
这两种情况都是不行的
将圆展成一条链,跨越端点的线段就改为它的补集
这样问题就变成了在一坨线段里选一些没有相离或包含关系的线段,并且数量最多
将线段左端点排序,然后就变成了求关于右端点的最长上升子序列
但是不能相离?
强制一个线段选,做n次,这样求最长上升子序列的过程用二分优化一下
时间复杂度 O(n2logn)

第一次在bz上rk1,超鸡冻

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

const double pi=acos(-1.0);
const double inf=1e18;
int n,tmp,ans;double R,x,y,len,ang1,ang2,stack[2005];
struct data{double x,y;bool flag;}s[2005];

int cmp(data a,data b)
{
    return a.flag<b.flag||(a.flag==b.flag&&a.x<b.x)||(a.flag==b.flag&&a.x==b.x&&a.y<b.y);
}
int find(double x)
{
    int l=0,r=tmp,mid,ans=0;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (stack[mid]>=x) ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}
int main()
{
    scanf("%d%lf",&n,&R);
    for (int i=1;i<=n;++i)
    {
        scanf("%lf%lf",&x,&y);
        len=sqrt(x*x+y*y);
        if (len<=R)
        {
            s[i].flag=1;
            continue;
        }
        ang1=atan2(y,x);ang2=acos(R/len);
        s[i].x=ang1-ang2;s[i].y=ang1+ang2;
        if (s[i].y>pi) s[i].y-=2*pi,swap(s[i].x,s[i].y);
        if (s[i].x<-pi) s[i].x+=2*pi,swap(s[i].x,s[i].y);
    }
    sort(s+1,s+n+1,cmp);
    for (int i=1;i<=n;++i)
        if (s[i].flag) {n=i-1;break;}
    stack[0]=-inf;
    for (int i=1;i<=n;++i)
    {
        if (s[i].flag) break;
        stack[tmp=1]=s[i].y;
        for (int j=i+1;j<=n;++j)
            if (s[j].x>s[i].y) break;
            else
            {
                if (s[j].y>stack[tmp])
                {
                    stack[++tmp]=s[j].y;
                    continue;
                }
                int t=find(s[j].y);
                if (t!=1) stack[t]=s[j].y;
            }
        ans=max(ans,tmp);
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值