LibreOJ #6177. 「美团 CodeM 初赛 Round B」送外卖2【状压DP】

「美团 CodeM 初赛 Round B」送外卖2

内存限制:32 MiB
时间限制:200 ms

题目描述

一 张 n 个 点 m 条 有 向 边 的 图 上 , 有 q 个 配 送 需 求 , 需 求 的 描 述 形 式 为 (si,ti,li,ri), 即需要从点 si 送到 ti, 在时刻 li 之后( 包括 li ) 可以在 si 领取货物, 需要在时刻 ri 之前( 包括 ri) 送达 ti, 每个任务只需完成一次。
图上的每一条边均有边权, 权值代表通过这条边消耗的时间。 在时刻 0 有一个工作人员在点 1 上, 求他最多能完成多少个配送任务。
在整个过程中, 可以认为领货跟交货都是不消耗时间的, 时间只花费在路程上。 当然在一个点逗留也是允许的。

输入格式

第一行, 三个正整数 n,m,q(2≤n≤20,1≤m≤400,1≤q≤10)
接下来 m 行, 每行三个正整数 ui,vi,ci(1≤ui,vi≤n,1≤ci≤20000)表示有一条从ui 到 vi 耗时为 ci 的有向边。
接下来 q 行, 每行四个正整数 si,ti,li,ri(1≤si,ti≤n,1≤li≤ri≤10^6), 描述一个配送任务。

输出格式

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

样例

样例输入

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

样例输出

2

样例解释

工作人员可以在时刻 1 到达点 2 ,领取第二个货物后在时刻 2 到达点 3 后交货,逗留到时刻 4 ,领取第三个货物,在时刻 4 到达点 4 并交货。

解题报告

每个货物的装填无非是三种:
1. 没拿
2. 拿在手上
3. 送到了
那么就简单了,写一个状压DP,压三位,表示三个状。我们肯定走最短路径,那么就先刷一趟最短路,然后分类讨论,就可以了。

代码如下

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 25
using namespace std;
int Ans,n,m,q,Three[MAXN],All,dst[MAXN][MAXN],INF;
struct xcw{int S,T,l,r;}a[MAXN];
int f[MAXN][60005*3];
void work(){
    memset(f,63,sizeof(f));INF=f[0][0];f[1][0]=0;
    for(int k=0;k<All;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=q;j++){
        int t=k%Three[j+1]/Three[j];
        if(t==0) if(f[i][k]+dst[i][a[j].S]<=a[j].r) f[a[j].S][k+Three[j]]=min(f[a[j].S][k+Three[j]],max(a[j].l,f[i][k]+dst[i][a[j].S]));
        if(t==1) if(f[i][k]+dst[i][a[j].T]<=a[j].r) f[a[j].T][k+Three[j]]=min(f[a[j].T][k+Three[j]],           f[i][k]+dst[i][a[j].T] );
    }
}
int main(){
//  freopen("prob.in","r",stdin);
//  freopen("prob.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    memset(dst,63,sizeof(dst));
    for(int i=1;i<=n;i++) dst[i][i]=0;
    for(int i=1;i<=m;i++){
        int x,y,w;scanf("%d%d%d",&x,&y,&w);
        dst[x][y]=min(dst[x][y],w);
    }
    for(int i=1;i<=q;i++) scanf("%d%d%d%d",&a[i].S,&a[i].T,&a[i].l,&a[i].r);
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++) dst[i][j]=min(dst[i][j],dst[i][k]+dst[k][j]);
    Three[1]=1;
    for(int i=2;i<=q+1;i++) Three[i]=Three[i-1]*3;
    All=Three[q+1];work();
    for(int k=0;k<All;k++)
    for(int i=1;i<=n;i++)
    if(f[i][k]<INF){
        int Now=0;
        for(int j=1;j<=q;j++) Now+=((k%Three[j+1]/Three[j])==2);
        Ans=max(Ans,Now);
    }
    printf("%d\n",Ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值