「美团 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;
}