1. 程式人生 > >BZOJ1591:[Usaco2008 Dec]Largest Fence 最大的圍欄——題解

BZOJ1591:[Usaco2008 Dec]Largest Fence 最大的圍欄——題解

所有 logs get i++ pre ons 作者 極角 blog

http://www.lydsy.com/JudgeOnline/problem.php?id=1591

有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/+


+++++++++++++++++++++++++++++++++++++++++++

 

BZOJ1591:[Usaco2008 Dec]Largest Fence 最大的圍欄——題解