选拔赛——旅游

  传送门

题目大意:路上一共有三种风景点A,B,C,然后从1号点出发经过至少一个B和C类风景点,再回到1号点,然后问最短路径是多少?

首先有3种风景点,因此对应就有8种状态(状态表示的是到某个点经过的风景点有哪些),到每个点都有8种状态,记录状态的话用的是dis数组的第二维(将第二维j转化为一个二进制数对应有3位,第一位表示的是A类风景点的状态,第二位表示的是B类风景点的状态,第三位表示的是C类风景点的状态,对应的数位若是1表示有经过,0表示没经过),定义dis[i][j]表示在j状态下到达i号点的最短路径,然后就用dij递推出dis[1][7]和dis[1][6](7表示的状态是111,6表示的状态是110,都是经过了B,C风景点的),挑个最小的即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=200010;
ll inf=1e15;
struct node
{
    int v;
    ll w;
    node(int vv=0,ll ww=0)
    {
        v=vv;
        w=ww;
    }
};
struct state
{
    int v,sta;
    ll d;
    state(int vv=0,int staa=0,ll dd=0)
    {
        v=vv;
        sta=staa;
        d=dd;
    }
    friend bool operator <(const state&a,const state&b)
    {
        return a.d>b.d;
    }
};
vector<node>g[maxn];
ll dis[maxn][10];//dis[i][j]表示j状态下到达i节点的最短路径
int vis[maxn][10],val[maxn],n,m;
void dij()
{
    for(int i=1;i<=n;i++)
        for(int j=0;j<=8;j++)
            dis[i][j]=inf,vis[i][j]=0;
    dis[1][1<<val[1]]=0;
    priority_queue<state>q;
    while(!q.empty()) q.pop();
    q.push(state(1,1<<val[1],0));
    while(!q.empty())
    {
        state now=q.top();
        q.pop();
        int u=now.v,sta=now.sta;
        if(vis[u][sta]) continue;
        vis[u][sta]=1;
        for(int i=0;i<g[u].size();i++)
        {
            int v=g[u][i].v,nxtsta=sta|(1<<val[v]);
            ll w=g[u][i].w;
            if(!vis[v][nxtsta]&&dis[v][nxtsta]>(dis[u][sta]+w))
            {
                dis[v][nxtsta]=dis[u][sta]+w;
                q.push(state(v,nxtsta,dis[v][nxtsta]));
            }


        }
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&val[i]);
    }
    while(m--)
    {
        int u,v;
        ll d;
        scanf("%d %d %lld",&u,&v,&d);
        g[u].push_back(node(v,d));
        g[v].push_back(node(u,d));
    }
    dij();
    printf("%lld
",min(dis[1][7],dis[1][6]));
    return 0;
}

  

原文地址:https://www.cnblogs.com/eason9906/p/11754888.html