hdu4892 Defence of the Trees 最短路

      平面上有k种树,给出所有树的坐标,并给出M个木桩的坐标,求以木桩为顶点,围成包含所有K种树的最小简单多边形的周长。思路基本就是官方题解给的思路,贴个链接:http://blog.sina.com.cn/s/blog_6bddecdc0102uyp3.html  感觉这种想法挺新颖的,之前都没有遇到过....

      简单解释一下,首先最优解一定是一个凸多边形,因为如果一个凹多边形符合,删掉那个凹进去的点一样符合要求并且周长更短..那么一个凸多边形一定存在某种三角剖分,那么M个木桩就会构成最多C(3,M)个三角形(注意特判掉三点共线的情况),以三角形作为顶点,存在一条公共边的两个三角形A,B之间连边,从A转移到B的边权及B的两条非公共边的和减去公共边的长度,例如

1    2

3    4

对于这四个点,S123--》S234,边权及L24+L34-L23.

建好图以后,就是要在图找一条节点包含了所有K种树的最短路了...

这个起点没有固定,但也不能去枚举所有的节点为起点,可以枚举起点的状态,求2^k次最短路,这样复杂度就可以接受了...

代码写的太渣..在hdu上喜闻乐见的垫底了...

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
using namespace std;
const double PI=acos(-1.0);
const double eps=1e-10;
const double inf=1e15;
struct Point
{
    double x,y;
    int k;
    Point(){}
    Point(double a,double b,int c)
    {
        x=a;
        y=b;
        k=c;
    }
    Point(double a,double b)
    {
        x=a; y=b; k=-1;
    }
    void read()
    {
        scanf("%lf%lf",&x,&y);
    }
    void print()
    {
        printf("%.6lf %.6lf\n",x,y);
    }

};
typedef Point Vector;
Vector operator + (Vector A,Vector B)
{
    return Vector(A.x+B.x,A.y+B.y);
}
Vector operator - (Point A,Point B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator * (Vector A,double p)
{
    return Vector(A.x*p,A.y*p);
}
Vector operator / (Vector A,double p)
{
    return Vector(A.x/p,A.y/p);
}
bool operator < (const Point &a,const Point &b)
{
    return a.x<b.x || (a.x==b.x && a.y<b.y);
}

int dcmp(double x)
{
    if (fabs(x)<eps) return 0;
    else return x<0?-1:1;
}
bool operator == (const Point& a,const Point b)
{
    return dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0;
}
//atan2(x,y) :向量(x,y)的极角,即从x轴正半轴旋转到该向量方向所需要的角度。
double Dot(Vector A,Vector B)
{
    return A.x*B.x+A.y*B.y;
}
double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
double Length(Vector A)
{
    return sqrt(Dot(A,A));
}
bool PointInPolygon(Point p,Point a,Point b,Point c)
{
    double c1=Cross(b-a,p-a);
    double c2=Cross(c-b,p-b);
    double c3=Cross(a-c,p-c);
    if (c1<0 && c2<0 && c3<0) return true;
    if (c1>0 && c2>0 && c3>0) return true;
    return false;

}
bool line(Point a,Point b,Point c)
{
    if (dcmp(Cross(b-a,c-a))==0) return true;
    return false;
}
double TriLength(Point a,Point b,Point c)
{
    return (Length(b-a)+Length(c-b)+Length(a-c));
}
const int maxm=42;
const int maxn=23000;
struct node
{
    int v,next;
    double w;
}edge[505000];
int sta[maxn];
int g[maxn];
Point tree[330];
Point s[44];
int n,m,k;
int N,en;
int num[maxm][maxm][maxm];
double Tlen[maxn];
void addedge(int x,int y,double w)
{
    edge[en].v=y;
    edge[en].w=w;
    edge[en].next=g[x];
    g[x]=en++;
}
double dis[66][maxn];
bool inq[66][maxn];
struct Node
{
    int s,u;
    Node(){}
    Node(int x,int y)
    {
        s=x; u=y;
    }
};
double spfa(int st)
{
    queue<Node> q;
    memset(inq,false,sizeof inq);
    for (int j=0; j<N; j++)
    if (dcmp(dis[st][j])<inf)
    {
        q.push(Node(st,j));
        inq[st][j]=true;
    }
    while(!q.empty())
    {
        Node tp=q.front(); q.pop();
        inq[tp.s][tp.u]=false;
        int u=tp.u;
        int v;
        int ts=0;
        for (int j=g[u]; j!=-1; j=edge[j].next)
        {
            v=edge[j].v;
            ts=(tp.s|sta[v]);
            if (dis[ts][v]>dis[tp.s][tp.u]+edge[j].w)
            {
                dis[ts][v]=dis[tp.s][tp.u]+edge[j].w;
                if (!inq[ts][v])
                {
                    inq[ts][v]=true;
                    q.push(Node(ts,v));
                }
            }
        }
    }
    double res=inf;
    for (int i=0; i<N; i++)
    res=min(res,dis[(1<<k)-1][i]);
    return res;
}
double slove()
{
    double res=inf;
    for (int i=0; i<(1<<k); i++)
    {
        for (int ii=0; ii<(1<<k); ii++)
         for (int j=0; j<N; j++)
         dis[ii][j]=inf;

        for (int j=0; j<N; j++)
        if (sta[j]==i) dis[i][j]=Tlen[j];

        res=min(res,spfa(i));
    }
    return res;
}

int main()
{
//    freopen("in.txt","r",stdin);
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        memset(num,-1,sizeof num);
        for (int i=0; i<n; i++)
        tree[i].read();
        for (int i=0; i<n; i++)
        {
            scanf("%d",&tree[i].k);
            tree[i].k--;
        }
        for (int i=0; i<m; i++)
        {
            s[i].read();
        }
        N=0;
        for (int i1=0; i1+2<m; i1++)
            for (int i2=i1+1; i2+1<m; i2++)
                for (int i3=i2+1; i3<m; i3++)
                if (!line(s[i1],s[i2],s[i3]))
                {
                    Tlen[N]=TriLength(s[i1],s[i2],s[i3]);
                    num[i1][i2][i3]=N;
                    int ss=0;
                    for (int j=0; j<n; j++)
                    if (PointInPolygon(tree[j],s[i1],s[i2],s[i3]))
                    {
                        ss|=(1<<tree[j].k);
                    }
                    sta[N]=ss;
                    N++;
                }
        for (int i1=0; i1<m; i1++)
         for (int i2=0; i2<m; i2++)
         if (i1!=i2)
          for (int i3=0; i3<m; i3++)
          if (i1!=i3 && i2!=i3)
          {
              if (i1<i2 && i2<i3) continue;

              int xx[4];
              xx[0]=i1; xx[1]=i2; xx[2]=i3;
              sort(xx,xx+3);
              num[i1][i2][i3]=num[xx[0]][xx[1]][xx[2]];
          }

         
        memset(g,-1,sizeof g);
        en=0;
        for (int i1=0; i1+1<m; i1++)
         for (int i2=i1+1; i2<m; i2++)
         {
             for (int i3=0; i3<m; i3++)
             if (i1!=i3 && i2!=i3)
             for (int i4=0; i4<m; i4++)
             if (i1!=i4 && i2!=i4 && i3!=i4)
             {
                 if (num[i1][i2][i3]!=-1 && num[i1][i2][i4]!=-1)
                 {
                     if (dcmp(Cross(s[i2]-s[i1],s[i3]-s[i1])*Cross(s[i2]-s[i1],s[i4]-s[i1]))==-1)
                     {
                         double len=Length(s[i4]-s[i1])+Length(s[i4]-s[i2])-Length(s[i2]-s[i1]);
                         addedge(num[i1][i2][i3],num[i1][i2][i4],len);
                     }
                 }
             }
         }
        double ans=slove();
        if (dcmp(fabs(inf-ans))==1) printf("%.10lf\n",ans);
        else puts("Impossible");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值