[usaco] 2008 Dec Largetst Fence 最大的围栏 2 || dp

本文介绍了一种解决平面上n个点组成的凸多边形中包含最多顶点数问题的方法。通过枚举凸包的左下角进行极角排序,并使用动态规划算法优化至n³的时间复杂度。

原网站大概已经上不了了……

题目大意:
求出平面上n个点组成的一个包含顶点数最多的凸多边形。n<=250.


考虑我们每次枚举凸包的左下角为谁(参考Graham求凸包时的左下角),然后像Graham一样,将其他点相对于当前左下角做一个极角排序。
我们会想到一个dp,dp[i][j]表示当前边为i到j的最多点,得到下面这样的转移:

for (int i=1;i<=n;i++)
    if (x!=i) dp[x][i]=1;
for (int i=1;i<=n;i++)
    for (int j=i+1;j<=n;j++)
        for (int k=1;k<i;k++)
            if (check(k,i,j)) dp[i][j]=max(dp[i][j],dp[k][i]+1);
//check是检查是否为“凸”

而这样就是n^4了,怎么优化呢?
我们可以记录f[i]为dp[][i]的最大值,这样就省去了枚举k的n,就得到了n^3算法!
因为要省去枚举k的过程,所以check怎么办?
我们预处理每条线段,然后将线段按极角排序即可~

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 300
using namespace std;
int n,q[N],f[N],dp[N][N],num,ans;
struct hhh
{
    int x,y;
    hhh() : x(0),y(0) {}
    hhh(int _x,int _y) : x(_x),y(_y) {}
    hhh operator - (const hhh &b) const
    {
        return hhh(x-b.x,y-b.y);
    }
    int operator * (const hhh &b) const
    {
        return x*b.y-y*b.x;
    }
}a[N];
struct hh
{
    hhh qwq;
    int a,b;
    bool operator < (const hh &b) const
    {
        return qwq*b.qwq<0;
    }
}v[N*N];

int read()
{
    int ans=0,fu=1;
    char j=getchar();
    for (;j<'0' || j>'9';j=getchar()) if (j=='-') fu=-1;
    for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
    return ans*fu;
}

int check(int x)
{
    memset(f,128,sizeof(f));
    memset(dp,0,sizeof(dp));
    f[x]=0;
    int QwQ=0;
    for (int i=1;i<=num;i++)
    {
        dp[v[i].a][v[i].b]=max(dp[v[i].a][v[i].b],f[v[i].a]+1);
        if (v[i].b!=x) f[v[i].b]=max(f[v[i].b],f[v[i].a]+1);
    }
    for (int i=1;i<=n;i++)
        QwQ=max(QwQ,dp[i][x]);
    return QwQ;
}

int main()
{
    freopen("dot.in","r",stdin);
    freopen("dot.out","w",stdout);
    n=read();
    for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            if (i!=j)
                v[++num].qwq=a[i]-a[j],v[num].a=i,v[num].b=j;
    sort(v+1,v+num+1);
    for (int i=1;i<=n;i++)
        ans=max(ans,check(i));
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/mrha/p/8392483.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值