【状压DP】LibreOJ6177(美团 CodeM 初赛 Round B)[送外卖2]题解

来源:互联网 发布:nginx 根目录 跳转 编辑:程序博客网 时间:2024/05/17 21:42

题目概述

n 个点( n<20 ), m 条边, q ( q<10 )个外卖 (s,t,l,r) 表示在 l 之后到 s 接外卖,在 r 之前到 t 送外卖。快递员刚开始在 1 ,求最多能够送多少外卖。

ps:可以同时带上许多快递。

解题报告

刚开始以为只能带一个快递……数据范围这么小……要么DFS要么状压DP……

先想DFS,发现想要实现“可以同时带多个快递”这个功能DFS就会很慢。所以我们想状压DP。

状压怎么做?记两个状态?显然是错的。对于每个外卖有三个状态: 0 没接, 1 接了, 2 送了。

那么其实就是比较简单的三进制状压DP,预处理一下 3x 就行了。

示例程序

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=20,maxq=10,maxs=59049;int n,m,q,dis[maxn+5][maxn+5],p[maxq+5];int s[maxn+5],t[maxn+5],l[maxn+5],r[maxn+5];int INF,f[maxn+5][maxs+5],ans;void Floyd(){    for (int i=1;i<=n;i++) dis[i][i]=0;    for (int k=1;k<=n;k++)    for (int i=1;i<=n;i++)    for (int j=1;j<=n;j++)        dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}#define pos(x,len) ((x)/p[len]%3)inline int Count(int x) {int tot=0;for (int i=0;i<q;i++) tot+=pos(x,i)==2;return tot;}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    scanf("%d%d%d",&n,&m,&q);memset(dis,63,sizeof(dis));    for (int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&x,&y,&z),dis[x][y]=min(dis[x][y],z);    for (int i=0;i<q;i++) scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]);    Floyd();p[0]=1;for (int i=1;i<=q;i++) p[i]=p[i-1]*3;    memset(f,63,sizeof(f));INF=f[0][0];f[1][0]=0;    for (int j=1;j<p[q];j++)    for (int i=0;i<q;i++)        if (pos(j,i)==1)        {            for (int k=1;k<=n;k++) if (f[k][j-p[i]]<INF&&f[k][j-p[i]]+dis[k][s[i]]<=r[i])                f[s[i]][j]=max(l[i],min(f[s[i]][j],f[k][j-p[i]]+dis[k][s[i]]));        } else        if (pos(j,i)==2)        {            for (int k=1;k<=n;k++) if (f[k][j-p[i]]<INF&&f[k][j-p[i]]+dis[k][t[i]]<=r[i])                f[t[i]][j]=min(f[t[i]][j],f[k][j-p[i]]+dis[k][t[i]]);        }    for (int j=0;j<p[q];j++)    for (int i=1;i<=n;i++)        if (f[i][j]<INF) ans=max(ans,Count(j));    return printf("%d\n",ans),0;}
阅读全文
1 0