百度之星度度熊保护村庄

比赛的时候并没有做出这道题。。
于是在赛后决定更正一下

把度度熊和它的伙伴们当成黑点,所有的村庄当成红点
考虑对所有的黑点建图
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做了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值