题目描述
一张 n n n个点 m m m 条有向边的图上,有 q q q 个配送需求,需求的描述形式为 ( s i , t i , l i , r i ) ( s_i , t_i , l_i , r_i ) (si,ti,li,ri),即需要从点 s i s_i si 送到 t i t_i ti, 在时刻 l i l_i li 之后(包括 l i l_i li )可以在 s i s_i si 领取货物,需要在时刻 r i r_i ri 之前(包括 r i r_i ri)送达 t i t_i ti ,每个任务只需完成一次。
图上的每一条边均有边权,权值代表通过这条边消耗的时间。在时刻 0 0 0 有一个工作人员在点 1 1 1 上,求他最多能完成多少个配送任务。
在整个过程中,可以认为领货跟交货都是不消耗时间的,时间只花费在路程上。当然在一个点逗留也是允许的。
输入格式
第一行,三个正整数
n
,
m
,
q
(
2
≤
n
≤
20
,
1
≤
m
≤
400
,
1
≤
q
≤
10
)
n , m , q (2\leq n\leq 20, 1\leq m\leq 400, 1\leq q\leq 10)
n,m,q(2≤n≤20,1≤m≤400,1≤q≤10)
接下来
m
m
m 行,每行三个正整数
u
i
,
v
i
,
c
i
(
1
≤
u
i
,
v
i
≤
n
,
1
≤
c
i
≤
20000
)
u_i , v_i , c_i (1\leq u_i,v_i\leq n, 1\leq c_i\leq 20000)
ui,vi,ci(1≤ui,vi≤n,1≤ci≤20000),表示有一条从
u
i
u_i
ui 到
v
i
v_i
vi 耗时为
c
i
c_i
ci 的有向边。
接下来
q
q
q 行,每行四个正整数
s
i
,
t
i
,
l
i
,
r
i
(
1
≤
s
i
,
t
i
≤
n
,
1
≤
l
i
≤
r
i
≤
1
0
6
)
s_i , t_i , l_i , r_i (1\leq s_i,t_i\leq n, 1\leq l_i\leq r_i\leq 10^6)
si,ti,li,ri(1≤si,ti≤n,1≤li≤ri≤106),描述一个配送任务。
输出格式
一个整数,表示最多能完成的任务数量。
样例
样例输入
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 并交货。
这题要配送的状态只有10个,显然是可以状压的。我们考虑二进制状压,表示每个点是否配送完成,发现其中还有一个取货的状态,比较难表示,所以二进制状压不是特别可行。于是我们考虑三进制状压, f [ i ] [ k ] f[i][k] f[i][k]表示当前在点 i i i,状态为 k k k的时候的最短时间。其中 k k k是一个三进制数,由 0 / 1 / 2 0/1/2 0/1/2组成,分别表示该配送没有取货,取了但没有送到,送到了。
由于
n
n
n只有
20
20
20,我们先用floyd算出最短路(
g
[
i
]
[
j
]
g[i][j]
g[i][j]),然后进转移。对于三进制状态的每一个0,它都可以转移到一个状态1,表示取了这个配送的货f[s[j]][k+p[j]]=min(f[s[j]][k+p[j]],max(f[i][k]+g[i][s[j]],l[j]));
,到状态
k
k
k的时间加上
k
k
k到
s
[
j
]
s[j]
s[j](陪送
j
j
j取货的位置)的最短时间,再与最小取货时间去
m
a
x
max
max即使从这种状态推过来的时间。
对于每一个状态1,它都可以推到一个状态2,但是前提是f[i][k]+g[i][t[j]]<=r[j]
,然后就可以进行转移f[t[j]][k+p[j]]=min(f[t[j]][k+p[j]],f[i][k]+g[i][t[j]])
,转移方程的思路类似于1的思路。
边转移边记录状态为2的数,最后取 m a x max max即可。
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,q,cnt,ans,f[22][60000],p[12];
int s[12],t[12],l[12],r[12],g[22][22];
int main()
{
n=read();m=read();q=read();p[1]=1;
for(int i=2;i<=q+1;i++)p[i]=p[i-1]*3;cnt=p[q+1];
for(int i=1;i<=n;i++)
for(int j=0;j<=p[q+1];j++)f[i][j]=inf;f[1][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)g[i][j]=(i==j?0:inf);
for(int i=1;i<=m;i++){
int x=read(),y=read(),c=read();
g[x][y]=min(g[x][y],c);
}
for(int i=1;i<=q;i++){
s[i]=read();t[i]=read();l[i]=read();r[i]=read();
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
for(int k=0;k<cnt;k++)
for(int i=1;i<=n;i++){
if(f[i][k]==inf) continue;
int tot=0;
for(int j=1;j<=q;j++){
int T=k/p[j]%3;
if(T==0){
f[s[j]][k+p[j]]=min(f[s[j]][k+p[j]],max(f[i][k]+g[i][s[j]],l[j]));
}
else if(T==1){
if(f[i][k]+g[i][t[j]]<=r[j]) f[t[j]][k+p[j]]=min(f[t[j]][k+p[j]],f[i][k]+g[i][t[j]]);
}
else tot++;
}
ans=max(ans,tot);
}
printf("%d",ans);
return 0;
}