2013 Multi-University Training Contest 1 Occupy Cities HDU 4606

29 篇文章 0 订阅

题意:给你N个城市,M个线段障碍,P个士兵,所有城市和士兵都是建立在平面坐标系上,士兵身上都有一个一定容量的资源背包,每次占领一个城市,中间消耗的资源为移动到那个星球之间的最短距离,当然不能穿过障碍(士兵刚开始空降到一个城市的时候是不需要花费的)。一个士兵占领一个城市后可以填满背包然后继续去占领其他城市。求最少背包容量为多少,使得最后p个士兵可以占领N个城市。

题解:把平面上所有可以沿直线到达的点连边 (与所给的线段判相交)(为了求出两两之间最短路连边),然后floyd求一遍任意两点间的最短路。二分答案,对可以一步到达(即两个城市之间的距离小于等于当前答案时)的城市进行连边(注意题目给出的城市先后顺序),随后问题就转为 一个经典的最小路径覆盖了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int maxn=305;
const int maxm=100000;
int to[maxm],next[maxm],head[maxn],edge;
bool vis[maxn];
int match[maxn],n,p,m;
double dis[maxn][maxn];
int que[maxn];
struct point
{
    int x,y;
} data[maxn];
int Direction(point pi,point pj,point pk)
{
    return (pj.x-pi.x)*(pk.y-pi.y)-(pk.x-pi.x)*(pj.y-pi.y);
}
bool Segment_Intersect(point p1,point p2,point p3,point p4)
{
    int d1=Direction(p3,p4,p1),d2=Direction(p3,p4,p2),d3=Direction(p1,p2,p3),d4=Direction(p1,p2,p4);
    if(((d1>0&&d2<0)||(d1<0&&d2>0))&&((d3>0&&d4<0)||(d3<0&&d4>0)))
        return 1;
    return 0;
}
inline double Dis(int i,int j)
{
    return sqrt((double)(data[i].x-data[j].x)*(data[i].x-data[j].x)+(double)(data[i].y-data[j].y)*(data[i].y-data[j].y));
}
inline void add(int u,int v)
{
    to[edge]=v,next[edge]=head[u],head[u]=edge++;
}
bool find(int now)
{
    int i,v;
    for(i=head[now]; ~i; i=next[i])
        if(!vis[v=to[i]])
        {
            vis[v]=true;
            if(match[v]==-1||find(match[v]))
            {
                match[v]=now;
                return true;
            }
        }
    return false;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&p);
        int cnt=n+2*m;
        for(int i=1; i<=n; i++)
            scanf("%d%d",&data[i].x,&data[i].y);
        for(int j=n+1; j<=n+2*m; j++)
            scanf("%d%d",&data[j].x,&data[j].y);
        for(int i=1; i<=n; i++)
            scanf("%d",&que[i]);
        for(int i=1; i<=cnt; i++)
            for(int j=i; j<=cnt; j++)
            {
                if(i==j) dis[i][j]=0;
                else dis[i][j]=dis[j][i]=Dis(i,j);
            }
        for(int j=1; j<=cnt; j++)
            for(int k=j+1; k<=cnt; k++)
                for(int i=n+1; i<=cnt; i+=2)
                {
                    if(dis[j][k]!=-1&&Segment_Intersect(data[i],data[i+1],data[j],data[k]))
                    {
                        dis[k][j]=dis[j][k]=-1;
                        break;
                    }
                }
        for(int k=1; k<=cnt; k++)
            for(int i=1; i<=cnt; i++)
                if(dis[i][k]!=-1)
                    for(int j=1; j<=cnt; j++)
                        if(dis[k][j]!=-1)
                        {
                            if(dis[i][j]==-1) dis[i][j]=dis[i][k]+dis[k][j];
                            else if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];
                        }
        double ans=20000*sqrt(2.0);
        double l=0.0,r=ans;
        while(r-l>1e-8)
        {
            double mid=(l+r)/2.0;
            memset(head,-1,sizeof(head));
            memset(match,-1,sizeof(match));
            edge=0;
            for(int i=1; i<=n; i++)
                for(int j=i+1; j<=n; j++)
                    if(dis[que[i]][que[j]]!=-1&&dis[que[i]][que[j]]<=mid)
                        add(que[i],que[j]+n);
            int res=0;
            for(int i=1; i<=n; i++)
            {
                memset(vis,0,sizeof(vis));
                if(find(i))
                    res++;
            }
            if(n-res<=p) {ans=mid;r=mid;}
            else l=mid;
        }
        printf("%.2f\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值