BZOJ1591 & 洛谷2924:[USACO2008 DEC]Largest Fence 最大的围栏——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1591
https://www.luogu.org/problemnew/show/P2924#sub
有n(5≤n≤250)个栅栏点,FJ需要围成一个栅栏圈,这个圈是一个凸包并且凸包上的点最多。
这题题解写的如此之玄幻,题目看起来如此之不可做……
然而实际很简单……看了半天题解发现不如看他代码。
我们考虑我们是怎么构造凸包的,就是通过点的极角排序来判断这个点所引出的线应该往哪里转(或者可能不取这个点)……
那么我们显然可以对边进行极角排序,然后利用这些边来构造凸包。
首先我们枚举点a作为这个凸包的最左点,dp[i][j]表示(i,j)这条边作为凸包a的最后一条边时一共有多少点。
那么我们枚举k,显然如果满足极角排序中(k,i)<(i,j)那么(k,i)(i,j)就可以作为这个凸包的两条边,我们就可以dp[i][j]=max{dp[k][i]+1}
但是显然是O(n^4)的,考虑优化。
如果我们可以求出g[i]=max{dp[k][i]}显然可以O(n^2)。
于是对所有的边进行极角排序,枚举排序后的边端点x,y有g[y]=max(g[y],g[x]+1)(显然,可感性理解,初值g[a]=0,其余为-INF),同时转移dp即可。
#include<cstdio> #include<queue> #include<cctype> #include<cstring> #include<stack> #include<cmath> #include<algorithm> using namespace std; const int N=260; const int INF=2147483647; struct point{ int x,y; }p[N],q[N]; struct line{ point v; int x,y; }v[N*N]; int n,cnt,per[N],l,g[N],dp[N][N]; inline point getmag(point a,point b){ point s; s.x=b.x-a.x;s.y=b.y-a.y; return s; } inline int multiX(point a,point b){ return a.x*b.y-b.x*a.y; } inline bool cmp(line a,line b){ return multiX(a.v,b.v)<0; } inline void init(){ memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++)g[i]=-INF; } int solve(){ int ans=0; for(int i=1;i<=n;i++){ init(); g[i]=0; for(int j=1;j<=cnt;j++){ int px=v[j].x,py=v[j].y; dp[px][py]=max(dp[px][py],g[px]+1); if(py!=i)g[py]=max(g[py],g[px]+1); } for(int j=1;j<=n;j++)ans=max(ans,dp[j][i]); } return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d%d",&p[i].x,&p[i].y); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i!=j){ v[++cnt].x=i; v[cnt].y=j; v[cnt].v=getmag(p[i],p[j]); } } } sort(v+1,v+cnt+1,cmp); printf("%d\n",solve()); return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++