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\n",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\n",ans);
    return 0;
}

 

posted @ 2018-09-30 10:00  Star_Feel  阅读(364)  评论(0编辑  收藏  举报