送外卖 拓扑排序+状压DP+最短路

题面


1.注意到K=20,这样的数据范围让人想到状压DP,而且允许进行K次Dijkstra算法。

2.题目中“公司规定了其中某些小区送餐的先后顺序,比如i小区的餐必须在给j小区送餐前送到”释放出明显的拓扑排序信号。

3.设 f[i][j] 表示在i表示的状态下终点为j时的最小路程和,容易写出状态转移方程:

f[en][j]=min{f[st][k]+dis[k][j]} ,其中dis[k][j]表示k到j的最短距离,这个可以用最短路算法处理。满足拓扑序列关系时才可以转移

#include
#include
#include
#define ll long long
#define MAXN 20005
#define MAXM 400005
#define Min(x,y) ((x
using namespace std;

int N,M,K,T,ok[25],Lim;
ll f[1050000][25],Ans=2e9;

inline int _R()
{
    char s=getchar();int v=0,sign=0;
    while((s!='-')&&(s>57||s<48))s=getchar();
    if(s=='-')sign=1,s=getchar();
    for(;s>47&&s<58;s=getchar())v=v*10+s-48;
    if(sign)v=-v;
    return v;
}

int en[MAXM],nex[MAXM],las[MAXN],len[MAXM],tot;
void ADD(int x,int y,int z)
{
    en[++tot]=y;
    nex[tot]=las[x];
    las[x]=tot;
    len[tot]=z;
}

int En[MAXM],Nex[MAXM],Las[MAXN],Tot;
void add(int x,int y)
{
    En[++Tot]=y;
    Nex[Tot]=Las[x];
    Las[x]=Tot;
}

ll dis[25][MAXN];
void Dijkstra(int s,int p)
{
    int i,x,y;
    ll z;
    priority_queueint> >Q;

    for(i=1;i<=N;i++)dis[p][i]=2e9;
    dis[p][s]=0;
    Q.push(make_pair(0,s));
    while(Q.size())
    {
        x=Q.top().second;
        z=Q.top().first;
        Q.pop();
        if(dis[p][x]!=-z)continue;
        for(i=las[x];i;i=nex[i])
        {
            y=en[i];
            if(dis[p][y]>dis[p][x]+len[i])
            {
                dis[p][y]=dis[p][x]+len[i];
                Q.push(make_pair(-dis[p][y],y));
            }
        }
    }
}

int D[400];
stack<int>S;
int main()
{
    int i,j,k,x,y,z,cnt=0,Size;

    N=_R();M=_R();K=_R();
    for(i=1;i<=M;i++)
    {
        x=_R();y=_R();z=_R();
        ADD(x,y,z);
        ADD(y,x,z);
    }
    T=_R();
    for(i=1;i<=T;i++)
    {
        x=_R();y=_R();
        add(x,y);
        D[y]++;
    }

    for(i=1;i<=K+1;i++)Dijkstra(i,i);

    for(i=2;i<=K+1;i++)if(!D[i])S.push(i);
    while(S.size())
    {
        x=S.top();S.pop();
        for(i=Las[x];i;i=Nex[i])
        {
            y=En[i];
            D[y]--;
            ok[y]=ok[y]|ok[x]|(1<2);
            if(!D[y])S.push(y);
        }
    }
    //ok[i]表示i要加入时的前提条件
    Lim=(1<1;

    for(i=0;i<=Lim;i++)
    for(j=0;j<=K+1;j++)f[i][j]=2e9;
    f[0][1]=0;
    for(i=0;i<=Lim;i++)
    {
        for(j=1;j<=K+1;j++)
        {
            if(f[i][j]==2e9)continue;
            for(k=2;k<=K+1;k++)
            {
                if(((i&ok[j])!=ok[j]))continue;
                f[i|(1<2)][k]=Min((f[i|(1<2)][k]),(f[i][j]+dis[j][k]));
            }
        }
    }

    for(i=1;i<=K+1;i++)Ans=Min(Ans,(f[Lim][i]+dis[i][N]));
    printf("%lld",Ans);
}

你可能感兴趣的:(状态压缩,拓扑排序,DP,最短路算法)