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;
}

探讨了在平面上存在n个点时,如何通过动态规划的方法计算可以构成的满足特定条件的单调多边形数量。该问题要求多边形的非相邻边没有公共点,任意两条边不相交,并且与任一平行于y轴的直线的交点为一个或两个。文章详细介绍了如何使用预处理直线与点的位置关系进行判断,以及动态规划状态转移的具体实现。
1096

被折叠的 条评论
为什么被折叠?



