zoukankan      html  css  js  c++  java
  • BZOJ1097: [POI2007]旅游景点atr

    【传送门:BZOJ1097


    简要题意:

      给出n个点,m条边的无向连通图,有k个必经点,有c组关系,每组关系输入x,y,保证x和y为必经点,代表要在x上逗留后才能在y上逗留(可以直接经过y点,这样就不算逗留),必经点的编号为2到k+1,求出从1点开始,经过k个必经点后,到达n点的最短距离


    题解:

      PS1:要搞清楚,经过一个点不代表在这个点逗留

      PS2:为了减次方,在代码处,我把点的编号改为0到n-1,必经点为1到k

      dijkstra+状压DP

      首先因为k很小,所以可以将每个必经点到所有点的距离都求出来并保存

      (AKC:SPFA是logM的,dijkstra是logN的)显然要用dijkstra更优秀

      然后考虑DP来做,设f[i][j]为i状态(表示k个必经点是否已经逗留过),最后一个逗留的必经点是j时的最短距离

      枚举i的每一个位置,记录0和1出现的位置,然后枚举j就枚举每个1出现的位置

      转移的时候,枚举当前要逗留的必经点就枚举0的位置,设为k

      那么怎么处理先后逗留顺序呢?

      我们设p[i]为要在第i个必经点逗留必须要达到的状态

      对于一对先后顺序x,y,就将p[y]|=(1<<(x-1))(这里的x-1是为了减次方)

      如果当前状态i&p[k]!=p[k],那就代表应该在k之前逗留的点还有没逗留的,所以i状态时不能逗留k

      最后枚举每个必经点作为最后的必经点,求出f[(1<<k)-1][i]+第i个必经点到终点的距离的最小值


    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    struct node
    {
        int x,y,d,next;
    }a[410000];int len,last[21000];
    void ins(int x,int y,int d)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].d=d;
        a[len].next=last[x];last[x]=len;
    }
    struct list
    {
        int x,d;
        friend bool operator < (list n1,list n2){return n1.d>n2.d;}
    };
    priority_queue<list> q;
    int d[31][21000],p[31];
    bool v[21000];
    int f[1100000][31];
    int p0[31],p1[31];
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        if(k==0)
        {
            
        }
        len=0;memset(last,0,sizeof(last));
        for(int i=1;i<=m;i++)
        {
            int x,y,d;
            scanf("%d%d%d",&x,&y,&d);x--,y--;
            //0~n-1
            ins(x,y,d);ins(y,x,d);
        }
        memset(d,63,sizeof(d));
        for(int i=0;i<=k;i++)
        {
            memset(v,false,sizeof(v));
            d[i][i]=0;
            q.push((list){i,0});
            while(q.empty()==0)
            {
                list tno=q.top();q.pop();
                int x=tno.x;
                if(v[x]==true) continue;
                v[x]=true;
                for(int k=last[x];k;k=a[k].next)
                {
                    int y=a[k].y;
                    if(d[i][y]>d[i][x]+a[k].d)
                    {
                        d[i][y]=d[i][x]+a[k].d;
                        q.push((list){y,d[i][y]});
                    }
                }
            }
        }
        if(k==0){printf("%d
    ",d[0][n-1]);return 0;}
        int c;scanf("%d",&c);
        for(int i=1;i<=c;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);x--,y--;
            p[y]|=1<<(x-1);
        }
        memset(f,63,sizeof(f));
        for(int i=1;i<=k;i++) if(p[i]==0) f[1<<(i-1)][i]=d[i][0];
        for(int i=0;i<(1<<k);i++)
        {
            int d0=0,d1=0;
            for(int j=1;j<=k;j++)
            {
                if((i&(1<<(j-1)))==0) p0[++d0]=j;
                else p1[++d1]=j;
            }
            for(int j=1;j<=d1;j++)
            {
                for(int k=1;k<=d0;k++)
                {
                    int x=p1[j],y=p0[k];
                    if((p[y]&i)!=p[y]) continue;
                    f[i+(1<<(y-1))][y]=min(f[i][x]+d[x][y],f[i+(1<<(y-1))][y]);
                }
            }
        }
        int ans=1<<30;
        for(int i=1;i<=k;i++) ans=min(ans,f[(1<<k)-1][i]+d[i][n-1]);
        printf("%d
    ",ans);
        return 0;
    }

     

  • 相关阅读:
    linux学习笔记
    随笔
    matlab自学笔记(3)—图像绘制与图像处理
    matlab自学笔记(1)安装与简介
    matlab自学笔记(2)函数的使用
    四轴飞行器
    小学生300道练习题程序及问题
    对运动软件——乐动力的评价
    软件工程随记
    Visual Studio 2013版本安装
  • 原文地址:https://www.cnblogs.com/Never-mind/p/9728162.html
Copyright © 2011-2022 走看看