problem
给出 n n n 个白点和 m m m 个黑点(原点 ( 0 , 0 ) (0,0) (0,0) 无色),黑白点的纵坐标均大于 0 0 0,且无三点共线。现在要找出一个必须包含原点的凸包,使得凸包中白点的数量减黑点的数量最大,求出这个最大值。
数据范围: n + m ≤ 100 n+m\le100 n+m≤100。
solution
一道凸包 d p dp dp的简单题。
为了方便,我们把一个凸包的价值定义为该凸包内白点数减黑点数。
我们先按照极角排序,设 g [ i ] [ j ] g[i][j] g[i][j] 表示点 i , j i,j i,j 和 ( 0 , 0 ) (0,0) (0,0) 围成的三角形的价值(不包含 i , j i,j i,j,在计算的时候单独考虑,不然会算重), f [ i ] [ j ] f[i][j] f[i][j] 表示从原点顺时针方向构建的部分凸包最后两个点为 j , i j,i j,i 的最大价值。
状态的转移很显然,就不赘述了。
时间复杂度 O ( n 3 ) O(n^3) O(n3)。
code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 105
#define ll long long
using namespace std;
int n,m;
struct point{
int x,y,T;
point (int _x=0,int _y=0):x(_x),y(_y){}
friend point operator+(const point &a,const point &b) {return point(a.x+b.x,a.y+b.y);}
friend point operator-(const point &a,const point &b) {return point(a.x-b.x,a.y-b.y);}
friend ll dot(const point &a,const point &b) {return (ll)a.x*b.x+(ll)a.y*b.y;}
friend ll cross(const point &a,const point &b) {return (ll)a.x*b.y-(ll)a.y*b.x;}
friend bool operator<(const point &a,const point &b) {return atan2(a.y,a.x)<atan2(b.y,b.x);}
}p[N];
int g[N][N],f[N][N];
void prework(){
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
for(int k=i+1;k<j;++k)
if(cross(p[i]-p[k],p[j]-p[k])>0)
g[i][j]+=p[k].T;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y),p[i].T=1;
for(int i=n+1;i<=n+m;++i) scanf("%d%d",&p[i].x,&p[i].y),p[i].T=-1;
n=n+m,sort(p+1,p+n+1),prework();
int ans=0;
for(int i=1;i<=n;++i){
f[i][0]=p[i].T;
for(int j=1;j<i;++j){
int Max=0;
for(int k=0;k<j;++k)
if(cross(p[k]-p[i],p[j]-p[i])>=0)
Max=max(Max,f[j][k]+g[j][i]+p[i].T);
ans=max(ans,(f[i][j]=Max));
}
}
printf("%d\n",ans);
return 0;
}