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

题目概述

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值