度度熊保护村庄
Accepts: 26
Submissions: 677
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 32768/32768 K (Java/Others)
把度度熊和它的伙伴们当成黑点,所有的村庄当成红点
考虑对所有的黑点建图
O(n²)暴力枚举所有的黑点点对,对于每个点对(a, b),O(n)检测所有红点
如果所有的红点都在点对(a, b)(a->b)的右侧,则a到b连接一条长度为1的单向边
如果所有的红点都在点对(a, b)(a->b)的左侧,则b到a连接一条长度为1的单向边
否则a和b不连边
具体如何判定?假设当前便利到黑点a和b,红点为c
红点c在a和b(a->b)的右侧则有
同理大于0说明在左侧,等于0说明三点共线
(注意三点共线的时候,如果c点在a和b的线段上直接连双向边,否则不连边)
之后可以得到一张图G
很好想到图中最小的环就是最优方案,令road[a][b]为点a到点b的最短路
跑一边floyd就好了,答案就是黑点个数m-min(road[i][i]) (1<=i<=m)
当然如果这个图中都不存在环,那么说明无法保护整个村庄,输出ToT
复杂度O(n^3),注意虽然n=500不过还有可能会超时。。优化下就好了
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 1044266558
typedef struct Point
{
int x, y;
Point operator - ( const Point &b ) const
{
Point c;
c.x = x-b.x; c.y = y-b.y;
return c;
}
double operator * ( const Point &b ) const
{
return x*b.y-y*b.x;
}
}Point;
Point h[505], s[505];
int n, m, ans, road[505][505];
bool Jud(Point x, Point y, Point z)
{
if((x.x<z.x && y.x<z.x) || (x.y<z.y && y.y<z.y) || (x.x>z.x && y.x>z.x) || (x.y>z.y && y.y>z.y))
return 1;
return 0;
}
int main(void)
{
int i, j, k, flag;
while(scanf("%d", &n)!=EOF)
{
memset(road, 62, sizeof(road));
for(i=1;i<=n;i++)
scanf("%d%d", &h[i].x, &h[i].y);
scanf("%d", &m);
for(i=1;i<=m;i++)
scanf("%d%d", &s[i].x, &s[i].y);
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
flag = 1;
for(k=1;k<=n;k++)
{
if((s[i]-s[j])*(s[i]-h[k])<0 || (s[i]-s[j])*(s[i]-h[k])==0 && Jud(s[i], s[j], h[k]))
{
flag = 0;
break;
}
}
if(flag)
road[i][j] = 1;
}
}
ans = inf;
for(k=1;k<=m;k++)
{
for(i=1;i<=m;i++)
{
if(road[i][k]==inf)
continue;
for(j=1;j<=m;j++)
road[i][j] = min(road[i][j], road[i][k]+road[k][j]);
}
}
for(i=1;i<=m;i++)
ans = min(ans, road[i][i]);
if(ans>m)
printf("ToT\n");
else
printf("%d\n", m-ans);
}
return 0;
}