链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4171
题解
我可以把这个图形分成上壳和下壳
f
i
j
f_{ij}
fij表示上壳的结束点是编号为
i
i
i的点,下壳的结束点是编号为
j
j
j的点
那么显然
1
1
1到
m
a
x
(
i
,
j
)
max(i,j)
max(i,j)这些点都已经包含在图形中了
假设不妨设
i
<
j
i<j
i<j,假设有如下情形

因为
i
<
j
i<j
i<j所以把第
j
+
1
j+1
j+1个点加入到下面的线段上可定是可以的
即
f
i
,
j
+
1
+
=
f
i
j
f_{i,j+1}+=f_{ij}
fi,j+1+=fij,如下

如果将第
j
+
1
j+1
j+1个点加入到上壳中,且不会使得第
i
+
1
i+1
i+1到第
j
j
j个点在线段新产生的线段的上方,就可以将第
j
+
1
j+1
j+1个点加入到上壳中,如下图
即
f
j
+
1
,
j
+
=
f
i
j
f_{j+1,j}+=f_{ij}
fj+1,j+=fij

反之则无法转移,如下图

代码
//dp、几何
#include <bits/stdc++.h>
#define maxn 55
#define eps 1e-8
#define cl(x) memset(x,0,sizeof(x))
#define ll long long
using namespace std;
struct point
{
double x, y;
point(double xx, double yy){x=xx, y=yy;}
point(){}
}pt[maxn], _(0.0,0.0);
struct vec
{
point pt;
double x, y;
vec(point p, double xx, double yy){pt=p, x=xx, y=yy;}
vec(){}
};
ll f[maxn][maxn], N;
bool operator<(point p1, point p2){return p1.x<p2.x-eps;}
double operator*(vec v1, vec v2){return v1.x*v2.y-v2.x*v1.y;}
vec operator-(point p1, point p2){return vec(_,p2.x-p1.x,p2.y-p1.y);}
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
return f*x;
}
void init()
{
ll i;
N=read();
for(i=1;i<=N;i++)pt[i].x=read(), pt[i].y=read();
sort(pt+1,pt+N+1);
}
void dp()
{
ll i, j, k, ans=0;
cl(f);
f[1][1]=1;
for(i=1;i<=N;i++)
{
for(j=1;j<=i;j++)
{
if(i==j and i!=1)continue;
f[i+1][j]+=f[i][j];
f[j][i+1]+=f[j][i];
if(i==j)continue;
for(k=j+1;k<=i;k++)if((pt[i+1]-pt[j])*(pt[k]-pt[j])<eps)break;
if(k>i)f[i][i+1]+=f[i][j];
for(k=j+1;k<=i;k++)if((pt[i+1]-pt[j])*(pt[k]-pt[j])>-eps)break;
if(k>i)f[i+1][i]+=f[j][i];
}
}
printf("%lld\n",f[N-1][N]+f[N][N-1]);
}
int main()
{
ll T=read();
while(T--)
{
init();
dp();
}
return 0;
}

本文介绍了一种使用动态规划(DP)解决特定凸包问题的方法。通过定义状态fij表示上壳结束于点i,下壳结束于点j时的情况,文章详细阐述了如何进行状态转移,包括将新点加入上壳或下壳的条件判断,以及对应的代码实现。
9892

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



