题目描述:
一张n个点m条有向边的图上,有q个配送需求,需求的描述形式为(si,ti,li,ri),即需要从点si送到ti,在时刻li之后(包括li)可以在si领取货物,需要在时刻ri之前(包括ri)送达ti,每个任务只需完成一次。图上的每一条边均有边权,权值代表通过这条边消耗的时间。在时刻0有一个工作人员在点1上,求他最多能完成多少个配送任务。在整个过程中,可以认为领货跟交货都是不消耗时间的,时间只花费在路程上。当然在一个点逗留也是允许的。
数据范围:
2≤n≤20
1≤m≤400
1≤q≤10
--------------------------------------------------我是华丽的分割线--------------------------------------------------
题解:
这是一道状态压缩DP大水题。
需要注意的是,给出的边都是有向边,且允许外卖小哥同时携带多个货物。
观察数据范围,发现数据特别小,考虑暴力枚举配送顺序全排列然后模拟,但是显然TLE...
于是乎......
考虑到每个任务都只有3种可能状态(未取货、已取货但未送达和已送达),可以写一个3进制的状压DP。但是由于3进制用起来有那么一点麻烦,且空间复杂度可以承受,便考虑使用2个二进制数来表示状态。于是乎就定义F[i][j][k]用来表示是当前状态所需的最少时间,i表示当前在所在节点的编号,j和k为二进制数,分别表示当前身上所携带的货物和未送达的货物。
接下来考虑状态转移。
对于当前的F[i][j][k],枚举下一个未完成的任务ta。如果该任务已经携带在身上了,那么判断一下从当前状态转移是否更优(当然大前提是现在立马敢去来得及),更优的话更新下一状态。如果该任务还没有携带到身上,那么就去取货,同样也需要判断一下从当前状态转移是否更优。
接下来就可以开始进行愉快的DFS了。
考虑当初DP的定义,不难发现答案就是所有可能状态(就是被刷到过的状态)中已完成任务数量的最大值。
最后再贴上C++代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,q,ans,F[21][1025][1025],dist[21][21],s[11],t[11],l[11],r[11]; void Update(int k) { int tot=0; for(int ta=1;ta<=q;ta++) if(!(k&(1<<ta-1))) tot++; if(tot>ans) ans=tot; } void DFS(int i,int j,int k) { Update(k);//更新答案 for(int ta=1;ta<=q;ta++) { if(k&(1<<ta-1))//第ta个任务未派送 { if(j&(1<<ta-1))//身上已经带了第ta个任务,把任务送达 { if(F[i][j][k]+dist[i][t[ta]]<F[t[ta]][j-(1<<ta-1)][k-(1<<ta-1)]&&F[i][j][k]+dist[i][t[ta]]<=r[ta]) { F[t[ta]][j-(1<<ta-1)][k-(1<<ta-1)]=F[i][j][k]+dist[i][t[ta]]; DFS(t[ta],j-(1<<ta-1),k-(1<<ta-1)); } } else//身上没有第ta个任务,去把任务取来 { if(max(F[i][j][k]+dist[i][s[ta]],l[ta])<F[s[ta]][j+(1<<ta-1)][k]) { F[s[ta]][j+(1<<ta-1)][k]=max(F[i][j][k]+dist[i][s[ta]],l[ta]); DFS(s[ta],j+(1<<ta-1),k); } } } } } int main() { scanf("%d%d%d",&n,&m,&q); memset(dist,63,sizeof(dist));memset(F,63,sizeof(F)); for(int i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(dist[a][b]>c) dist[a][b]=c; } for(int i=1;i<=q;i++) scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]); for(int k=1;k<=n;k++) //Floyd刷出每两点间的最短路 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(dist[i][k]+dist[k][j]<dist[i][j]) dist[i][j]=dist[i][k]+dist[k][j]; for(int i=1;i<=n;i++) dist[i][i]=0; F[1][0][(1<<q)-1]=0; DFS(1,0,(1<<q)-1); printf("%d\n",ans); return 0; }