[编程题] 送外卖2

时间限制:1秒

空间限制:32768K
美团外卖日订单数已经超过1200万,实时调度系统是背后的重要技术支撑,其中涉及很多复杂的算法。下面的题目是某类场景的抽象。

一张 n 个点 m 条有向边的图上,有 q 个配送需求,需求的描述形式为( s_i , t_i , l_i , r_i ),即需要从点 s_i 送到 t_i, 在时刻 l_i 之后(包括 l_i)可以在 s_i 领取货物,需要在时刻 r_i 之前(包括 r_i)送达 t_i ,每个任务只需完成一次。 图上的每一条边均有边权,权值代表外卖配送员通过这条边消耗的时间。在时刻 0 有一个配送员在 点 1 上,求他最多能完成多少个配送任务。
在整个过程中,我们忽略了取餐与最后给用户递餐的时间(实际场景中这两个时间是无法省略的),只考虑花费在路程上的时间。另外,允许在一个点逗留。
输入描述:

第一行,三个正整数 n , m , q (2 ≤ n ≤ 0, 1 ≤ m ≤ 400, 1 ≤ q ≤ 10)。
接下来 m 行,每行三个正整数 u_i , v_i , c_i (1 ≤ u_i,v_i ≤ n, 1 ≤ c_i ≤ 20000),表示有一条从 u_i 到 v_i 耗时为 c_i 的有向边。
接下来 q 行,每行四个正整数 s_i , t_i , l_i , r_i (1 ≤ s_i,t_i ≤ n, 1 ≤ l_i ≤ r_i ≤ 10^6),描述一个配送任务。

输出描述:

一个整数,表示最多能完成的任务数量。

输入例子1:

5 4 3
1 2 1
2 3 1
3 4 1
4 5 1
1 2 3 4
2 3 1 2
3 4 3 4

输出例子1:
2

解法:三进制状压DP。dp[i][j]表示三进制状态为i,最终走到j点的最小时间花费。那么状态对应一个位置是0表示没有取餐,是1表示取了餐没有送,2表示送到了,然后分析每一位上是0还是1进行状态转移,转移之前要预处理一下最短路,这个点比较少直接Floyd预处理一下即可。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
//dp【i】【j】表示三进制下状态为i,最终走到了j点的最小时间花费
//状态对应一个位子上是0表示还没有取餐,1表示取餐了还没送,2表示送到了
struct node{
    int from, to, l, r;
}b[410];
int a[410][410];
int n, m, q, three[12];
int dp[60000][30], digit[60000][13];
void pre_deal(){
    three[0] = 1;
    for(int i=1; i<11; i++) three[i] = three[i-1]*3;
    for(int i=0; i<three[10]; i++){
        int tmp = i;
        for(int j=0; j<10; j++){
            digit[i][j] = tmp%3;
            tmp /= 3;
        }
    }
    for(int i=0; i<60000; i++)
        for(int j=0; j<30; j++)
            dp[i][j] = inf;
    memset(a, 0x3f, sizeof(a));
    for(int i=1; i<=n; i++) a[i][i]=0;
}
void Floyd(){
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            for(int k=1; k<=n; k++)
                a[j][k]=min(a[j][k],a[j][i]+a[i][k]);
}
int main()
{
    while(~scanf("%d %d %d", &n, &m, &q))
    {
        pre_deal();
        for(int i=1; i<=m; i++){
            int u,v,c;
            scanf("%d %d %d", &u,&v,&c);
            a[u][v] = min(a[u][v], c);
        }
        Floyd();
        for(int i=0; i<q; i++){
            scanf("%d %d %d %d", &b[i].from,&b[i].to,&b[i].l,&b[i].r);
        }
        dp[0][1] = 0;
        int state = 1;
        for(int i=1; i<=q; i++) state *= 3;
        for(int i=0; i<state; i++){
            for(int j=1; j<=n; j++){
                if(dp[i][j]<inf){
                    for(int k=0; k<q; k++){
                        if(digit[i][k] == 0){
                            int nxt = i+three[k];
                            int temp;
                            if(dp[i][j]+a[j][b[k].from]>=b[k].l) temp = dp[i][j]+a[j][b[k].from];
                            if(dp[i][j]+a[j][b[k].from]<b[k].l) temp = b[k].l;
                            dp[nxt][b[k].from] = min(dp[nxt][b[k].from], temp);
                        }
                        else if(digit[i][k] == 1){
                            int nxt = i+three[k];
                            if(dp[i][j]+a[j][b[k].to]<=b[k].r){
                                dp[nxt][b[k].to] = min(dp[nxt][b[k].to],dp[i][j]+a[j][b[k].to]);
                            }
                        }
                        else continue;
                    }
                }
            }
        }
        int ans = 0;
        for(int i=0; i<state; i++){
            for(int j=1; j<=n; j++){
                if(dp[i][j]<inf){
                    int cnt = 0;
                    for(int k=0; k<q; k++){
                        if(digit[i][k] == 2){
                            cnt++;
                        }
                    }
                    ans = max(ans, cnt);
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}