[状压DP]LibreOJ #6177. 「美团 CodeM 初赛 Round B」送外卖2 题解

来源:互联网 发布:java九大排序算法 编辑:程序博客网 时间:2024/06/04 13:35

(传送门)

题目大意

已知一个有n个节点m条边的有向图,给出k个任务,每个任务从Li开始,到Ri结束,要求在Si接收货物并且在时间结束前Ti送达。(领货和交货不需要消耗时间),问最多能做多少个任务(注意,可以有多个货物在手上)。


解题分析

考虑到这题的k比较小,可以DFS或者是状压DP,DFS没什么好想法,那么开始思考状压DP。

如果求最多能完成几个任务有点烦,可以将货物情况进行状压,由于每个货物可能未送达,已到,又或者还没拿到,所以这道题是3进制存储状态。对于每位i,表示第i个节点的状态:0为未接收,1为已接收未送达,2为已送达。

因为如果送的时间越早,有更多的时间完成后面的任务,所以需要完成当前状态时所耗费的时间越少。但是完成这个状态时在哪?很明显完成状态时所在节点也要枚举。

但是如果走到一个节点然后什么都不会发生,那就是浪费时间。所以其实在枚举状态j之后不是枚举节点i而是任务i,这样只需传到s[i]或t[i],减少转移状态(因为很明显求最短路后直接连起来就行了,无需中间状态)。

上面有点乱,可以直接看转移方程(或者代码),比较清晰:

f[i][j]:当前状态为j(三进制表示),最后停留在i节点所需的最少时间。

如果当前是要接收这个货物i(对应三进制数位置为1)
f[s[i]][j]=max(L[i],min(f[u][jp[i]]+dis[u][s[i]]))
如果当前是要送达这个货物i(对应三进制数位置为2)
f[t[i]]][j]=min(f[u][jp[i]]+dis[u][t[i]])


复杂度

时间:O(nk3k); 空间:O(n3k);

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxs=59049;int n,e,k,ans,INF,dis[25][25],a[15],p[15],s[15],t[15],L[15],R[15],f[25][maxs+5];int po_s(int x,int len){return x/p[len]%3;}int _count(int x){int tot=0;for (int i=0;i<k;i++) tot+=(po_s(x,i)==2);/* printf("%d %d\n",x,tot); */return tot;}inline void readi(int &x){    x=0; char ch=getchar();    while ('0'>ch||ch>'9') ch=getchar();    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}}void Flyod(){    for (int i=1;i<=n;i++) dis[i][i]=0;    for (int t=1;t<=n;t++)        for (int i=1;i<=n;i++)            for (int j=1;j<=n;j++)                if (dis[i][j]>dis[i][t]+dis[t][j]) dis[i][j]=dis[i][t]+dis[t][j];}int main(){    freopen("c.in","r",stdin);    freopen("c.out","w",stdout);    readi(n); readi(e); readi(k); ans=0;    memset(dis,63,sizeof(dis));    for (int i=1,x,y,z;i<=e;i++){        readi(x); readi(y); readi(z);        if (z<dis[x][y]) dis[x][y]=z;    }    for (int i=0;i<k;i++) {readi(s[i]); readi(t[i]); readi(L[i]); readi(R[i]);}    Flyod(); p[0]=1; for (int i=1;i<=k;i++) p[i]=p[i-1]*3;    memset(f,63,sizeof(f)); INF=f[0][0]; f[1][0]=ans=0;    for (int j=1;j<p[k];j++)        for (int i=0,q;i<k;i++){            q=po_s(j,i);            switch (q){                case 1:                    for (int u=1;u<=n;u++) if (f[u][j-p[i]]<INF&&f[u][j-p[i]]+dis[u][s[i]]<=R[i])                        f[s[i]][j]=max(L[i],min(f[s[i]][j],f[u][j-p[i]]+dis[u][s[i]]));                break;                case 2:                    for (int u=1;u<=n;u++) if (f[u][j-p[i]]<INF&&f[u][j-p[i]]+dis[u][t[i]]<=R[i])                        f[t[i]][j]=min(f[t[i]][j],f[u][j-p[i]]+dis[u][t[i]]);                break;            }        }    /*for (int i=1;i<p[k];i++)        for (int j=1;j<=n;j++) printf("%d %d %d\n",i,j,f[i][j]);*/    for (int j=1;j<p[k];j++)        for (int i=1;i<=n;i++)            if (f[i][j]<INF) ans=max(ans,_count(j));    printf("%d",ans);    return 0;}
阅读全文
0 0
原创粉丝点击