LA 4258 Metal (递推)

7 篇文章 0 订阅

LA 4258 Metal


题目大意:

平面上有n个点,任意两点的x坐标不同.统计有多少种方案能将其连成单调多边形.满足多边形非相邻边不能有公共点,任意两条边不能相交,且与任意与y轴平行的直线与多边形的公共部分是一个点或一条线段(或者说该直线只能与多边形交于1个或者两个点).

题目分析:

由满足条件可知,对于多边形的上下边缘一定不会回折(如上图),因为若某条线延伸出去又折回来,那么一定会使得公共部分变成多条线段.所以两边经过的点x坐标一定是单调的.

还需要考虑是否发生相交的问题,直接预处理.判断两点间的点在这条直线的上方还是下方即可.

所以可以选择x坐标从小到大来递推,设dp(i,j)表示上边缘到达i点,下边缘到达j点,且0~max(i,j)的点都已经连起来的方案数.
要保证不重复不遗漏,那么下一个点就是t=max(i,j)+1.
当然需要注意到最终结点的方案数,否则方案数会被计算两次(从i到n-1和从j到n-1).

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxn=50+5;

struct Point {
    int x,y;
    bool operator < (const Point& rhs) const {
        return x<rhs.x;
    }
    void input() {
        scanf("%d%d",&x,&y);
    }
}P[maxn];

int Gu[maxn][maxn],Gd[maxn][maxn];

void init(int n)
{
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++) {
            Gu[i][j]=Gd[i][j]=1;
            double k=1.*(P[i].y-P[j].y)/(P[i].x-P[j].x);
            double b=P[i].y-k*P[i].x;
            for(int p=i+1;p<j;p++) {
                if(P[p].y>=k*P[p].x+b) Gu[i][j]=0;//若p在i-j直线上方,i-j不能形成上边缘 
                if(P[p].y<=k*P[p].x+b) Gd[i][j]=0;//若p在i-j直线下方,i-j不能形成下边缘 
            }
        }
}

int dp[maxn][maxn];//dp[i][j]表示上边缘到i点,下边缘到j点的方案数 

void solve(int n)
{
    memset(dp,0,sizeof(dp));
    dp[0][1]=dp[1][0]=1;
    for(int i=0;i<n-1;i++)//i和j不要循环到n-1,不然答案会被算两边 
        for(int j=0;j<n-1;j++) if(dp[i][j]) {
            int t=max(i,j)+1;
            if(t==n-1&&Gu[i][t]&&Gd[j][t]) {
                dp[n-1][n-1]+=dp[i][j];
                continue;
            }
            if(i!=n-1&&Gu[i][t]) dp[t][j]+=dp[i][j];
            if(j!=n-1&&Gd[j][t]) dp[i][t]+=dp[i][j];
        }
    printf("%d\n",dp[n-1][n-1]);
}

int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i=0;i<n;i++) P[i].input();
        sort(P,P+n);
        init(n);
        solve(n);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值