20个点的数据,基本上可以判断是状态压缩,所以就直接开始上手了。
基本思路还是很好想,构建出图之后用floyd构建全图联通,再用状压dp求解答案。
而难点在于,定义dp数组的含义。
我们回忆一下状压dp求解哈密顿路径的例题:
dp数组的两个维度表示的是当前状态和落脚点,值表示最短路径
当前状态包含了所有的已遍历点,落脚点是题目的限定条件(结束时要在n-1落脚)
那我们再看这道题,
显然这道题并不需要遍历所有点,而是要尝试遍历所有的订单
所以我们肯定将订单化为二进制表示的状态……emmm真的是二进制吗?
当然不是,每个订单有未接单,已接单和已送达三种状态
所以我们要用三进制表示这个数
那另外一维和值怎么处理呢?
我们在判定订单能否按时接单的时候,首先要看外卖小哥和商家的距离,再要看商家和客户的距离
所以我们需要的是:起点和距离
这样就比较好分析了:

我们定义f[i][j]为从i点出发,到达j状态需要花费的时间
当j状态对应的3进制位值为0时,则该状态未接单,为1时该状态已经接单,为2时该状态已送达

正解代码如下

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=22,M=70000;
long long f[N][M];
long long mp[N][N];
int n,m,q;
struct _{
    int s,t,l,r;
}task[22];
int ans=0;
long long tbit[12];//三进制数组,最大范围是10个三进制数,和最大订单数
int main(){
    tbit[0]=1;
    for(int i=1;i<12;i++){//构建三进制
        tbit[i]=tbit[i-1]*3;
    }
    cin>>n>>m>>q;
    memset(mp,0x3f,sizeof mp);
    for(int i=0;i<m;i++){
        int x,y,z;
        cin>>x>>y>>z;
        mp[x][y]=min(mp[x][y],(long long)z);
    }
    for(int i=0;i<q;i++){
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        task[i]={a,b,c,d};
    }
    for(int i=0;i<=n;i++) mp[i][i]=0;
    for(int l=1;l<=n;l++) //floyd求最短全连通
        for(int i=1;i<=n;i++) 
            for(int j=1;j<=n;j++) 
                mp[i][j]=min(mp[i][j],mp[i][l]+mp[l][j]);
    memset(f,0x3f,sizeof f);
    f[1][0]=0;
    for(int st=0;st<tbit[q];st++){//遍历所有状态
        for(int i=1;i<=n;i++){
            if(f[i][st]>=0x3f3f3f3f) continue;
            int sum=0;//记录该状态下的最大送达单数
            for(int j=0;j<q;j++){//遍历所有订单
                if((st/tbit[j])%3==0){//如果当前该订单未接单
                    //出发接单
                    f[task[j].s][st+tbit[j]]=min(f[task[j].s][st+tbit[j]],max(f[i][st]+mp[i][task[j].s],(long long)task[j].l));
                }
                else if((st/tbit[j])%3==1){//如果当前该订单已接单
                    //出发送单
                    if(f[i][st]+mp[i][task[j].t]<=task[j].r)
                        f[task[j].t][st+tbit[j]]=min(f[task[j].t][st+tbit[j]],f[i][st]+mp[i][task[j].t]);
                }
                else sum++;//如果当前订单已送达
            }
            ans=max(ans,sum);//更新状态
        }
    }
    cout<<ans;
}