比赛的时候并没有做出这道题。。 于是在赛后决定更正一下
把度度熊和它的伙伴们当成黑点,所有的村庄当成红点 考虑对所有的黑点建图 O(n²)暴力枚举所有的黑点点对,对于每个点对(a, b),O(n)检测所有红点 如果所有的红点都在点对(a, b)(a->b)的右侧,则a到b连接一条长度为1的单向边 如果所有的红点都在点对(a, b)(a->b)的左侧,则b到a连接一条长度为1的单向边 否则a和b不连边 (注意三点共线的时候,如果c点在a和b的线段上直接连双向边,否则不连边) 之后可以得到一张图G 很好想到图中最小的环就是最优方案,令road[a][b]为点a到点b的最短路 跑一边floyd就好了,答案就是黑点个数m-min(road[i][i]) (1<=i<=m) 当然如果这个图中都不存在环,那么说明无法保护整个村庄,输出ToT 复杂度O(n^3),注意虽然n=500不过还有可能会超时。。优化下就好了
然后就可以了。。 至于怎么判断左右边,就是一个差积的问题啦。。 然后就没有了 感觉很妙啊。。 一开始我是想对全部点建一个凸包,然后跑。。 但我只yy到一个盘TOT的做法,至于点数,还真想不到
#include<cstdio> #include<cstring> const int N=505; const int MAX=1<<28; int n,m; struct Node{int x,y;}; Node a[N],b[N];//住房 士兵 int f[N][N]; int mymin (int x,int y){return x<y?x:y;} bool check (Node a,Node b,Node c)//a是否不在b,c的中间 { if (a.x>b.x&&a.x>c.x) return true; if (a.x<b.x&&a.x<c.x) return true; if (a.y>b.y&&a.y>c.y) return true; if (a.y<b.y&&a.y<c.y) return true; return false; } double mu (Node a,Node b,Node c) { double x1=a.x-c.x,y1=a.y-c.y; double x2=b.x-c.x,y2=b.y-c.y; return x1*y2-x2*y1; } int main() { while (scanf("%d",&n)!=EOF) { for (int u=1;u<=n;u++) scanf("%d%d",&a[u].x,&a[u].y); scanf("%d",&m); for (int u=1;u<=m;u++) scanf("%d%d",&b[u].x,&b[u].y); for (int u=1;u<=m;u++) for (int i=1;i<=m;i++) f[u][i]=MAX; for (int u=1;u<=m;u++) { for (int i=1;i<=m;i++) { bool tf=true; for (int k=1;k<=n;k++) { if (mu(a[k],b[i],b[u])<0)//左边 tf=false; if (mu(a[k],b[i],b[u])==0&&check(a[k],b[i],b[u])) tf=false; if (!tf) break; } if (tf) f[u][i]=1; } } for (int u=1;u<=m;u++) for (int i=1;i<=m;i++) { if (f[i][u]==MAX) continue; for (int j=1;j<=m;j++) f[i][j]=mymin(f[i][j],f[i][u]+f[u][j]); } int ans=MAX; for (int u=1;u<=m;u++) ans=mymin(ans,f[u][u]); if (ans>m) printf("ToT\n"); else printf("%d\n",m-ans); } return 0; }话说可以在hdu做了
