「美团 CodeM 初赛 Round B」送外卖2---------------状压dp

题目描述

一张 n 个点 m 条有向边的图上,有 q  个配送需求,需求的描述形式为 (si,ti,li,ri)( s_i , t_i , l_i , r_i )(si?,ti?,li?,ri?),即需要从点 si 送到 ti, 在时刻 li 之后(包括 lil_ili? )可以在 sis_isi? 领取货物,需要在时刻 ri 之前(包括 ri)送达 ti ,每个任务只需完成一次。

图上的每一条边均有边权,权值代表通过这条边消耗的时间。在时刻 000 有一个工作人员在点 1 上,求他最多能完成多少个配送任务。

在整个过程中,可以认为领货跟交货都是不消耗时间的,时间只花费在路程上。当然在一个点逗留也是允许的。

输出格式

一个整数,表示最多能完成的任务数量。

样例输入

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 并交货。

    •   首先的首先,需要明确配送的方式。在配送的途中手中不一定只有一份外卖!

        然后出于对数据的敏感,易想到用进制数表示状态。

   •   由于每一份外卖有按照程序有三种状态,所以用三进制表示外卖的整体状态,

        在dp数组中作为一个维度,剩下时间和当前位置。

•    由于时间是数据中最大的存在,成为了dp的对象,当前位置成为了另一个状

态维度。

   •    转移的话,只是合法性的判断了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,q,x,y,z;
 4 int dis[25][25];
 5 int f[60050][25],w,now,ans;
 6 int base[15],s[15],t[15],l[15],r[15];
 7 int main()
 8 {
 9     base[0]=1;
10     for(int i=1;i<=11;++i)
11         base[i]=base[i-1]*3;
12     memset(dis,0x3f,sizeof(dis));
13     memset(f,0x3f,sizeof(f));f[0][1]=0;
14     scanf("%d%d%d",&n,&m,&q);
15     for(int i=1;i<=m;++i)
16     {
17         scanf("%d%d%d",&x,&y,&z);
18         dis[x][y]=min(dis[x][y],z);
19     }
20     for(int i=1;i<=n;++i)
21         dis[i][i]=0;
22     for(int k=1;k<=n;++k)
23         for(int i=1;i<=n;++i)
24             for(int j=1;j<=n;++j)
25                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
26     for(int i=0;i<q;++i)
27         scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]);
28     m=base[q]-1;
29     for(int i=0;i<=m;++i)
30     {
31         for(int j=1;j<=n;++j)
32         {
33             for(int k=0;k<q;++k)
34             {
35                 now=i/base[k]%3;
36                 if(now==0)
37                     f[i+base[k]][s[k]]=min(f[i+base[k]][s[k]],max(f[i][j]+dis[j][s[k]],l[k]));
38                 else if(now==1&&f[i][j]+dis[j][t[k]]<=r[k])
39                     f[i+base[k]][t[k]]=min(f[i+base[k]][t[k]],f[i][j]+dis[j][t[k]]);
40
41             }
42             if(f[i][j]<f[0][0])
43             {
44                 w=0;
45                 for(int k=0;k<=10;++k)
46                     if(i/base[k]%3==2)
47                         ++w;
48                 ans=max(ans,w);
49             }
50         }
51     }
52     printf("%d",ans);
53     return 0;
54 }

代码1

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,q,x,y,z;
 4 int dis[25][25];
 5 int f[60050][25],w,now,ans;
 6 int base[15],s[15],t[15],l[15],r[15];
 7 int main()
 8 {
 9     base[0]=1;
10     for(int i=1;i<=11;++i)
11         base[i]=base[i-1]*3;
12     memset(dis,0x3f,sizeof(dis));
13     memset(f,0x3f,sizeof(f));f[0][1]=0;
14     scanf("%d%d%d",&n,&m,&q);
15     for(int i=1;i<=m;++i)
16     {
17         scanf("%d%d%d",&x,&y,&z);
18         dis[x][y]=min(dis[x][y],z);
19     }
20     for(int i=1;i<=n;++i)
21         dis[i][i]=0;
22     for(int k=1;k<=n;++k)
23         for(int i=1;i<=n;++i)
24             for(int j=1;j<=n;++j)
25                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
26     for(int i=0;i<q;++i)
27         scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]);
28     m=base[q]-1;
29     for(int i=0;i<=m;++i)
30     {
31         for(int j=1;j<=n;++j)
32         {
33             if(f[i][j]==f[0][0])
34                 continue;
35             for(int k=0;k<q;++k)
36             {
37                 now=i/base[k]%3;
38                 if(now==0)
39                     f[i+base[k]][s[k]]=min(f[i+base[k]][s[k]],max(f[i][j]+dis[j][s[k]],l[k]));
40                 else if(now==1&&f[i][j]+dis[j][t[k]]<=r[k])
41                     f[i+base[k]][t[k]]=min(f[i+base[k]][t[k]],f[i][j]+dis[j][t[k]]);
42
43             }
44             w=0;
45             for(int k=0;k<=10;++k)
46                 if(i/base[k]%3==2)
47                     ++w;
48             ans=max(ans,w);
49         }
50     }
51     printf("%d",ans);
52     return 0;
53 }

代码2

  这两份代码在思路上是完全一致的。但是……

相似的结果还发生在另一个状压题上(第二份代码直接是TLE 了):

   这究竟是种怎样的操作?

    如果当前状态完全不可能被转移到的话,就完全没必要对它进行暴力扩展了,果断continue。

  

原文地址:https://www.cnblogs.com/wyher/p/9813393.html

时间: 2024-10-06 15:53:10

「美团 CodeM 初赛 Round B」送外卖2---------------状压dp的相关文章

#6177. 「美团 CodeM 初赛 Round B」送外卖2(floyed + 三进制枚举 )

 题目大意: 一张  个点  条有向边的图上,有  个配送需求,需求的描述形式为 ,即需要从点  送到 , 在时刻  之后(包括  )可以在  领取货物,需要在时刻  之前(包括 )送达  ,每个任务只需完成一次. 图上的每一条边均有边权,权值代表通过这条边消耗的时间.在时刻  有一个工作人员在点  上,求他最多能完成多少个配送任务. 在整个过程中,可以认为领货跟交货都是不消耗时间的,时间只花费在路程上.当然在一个点逗留也是允许的. 具体思路: 首先对全图跑一个floyed,求一下各点之间的最短

「美团 CodeM 初赛 Round B」景区路线规划 概率DP

题意 游乐园被描述成一张 n 个点,m 条边的无向图(无重边,无自环).每个点代表一个娱乐项目,第 i 个娱乐项目需要耗费 ci 分钟的时间,会让小 y 和妹子的开心度分别增加 h1i ,h2i ,他们俩初始的开心度都是 0 .每条边代表一条路,第 i 条边连接编号为 xi , yi 的两个娱乐项目,从 xi 走到 yi 或者从 yi 走到 xi 耗费的时间都是 ti 分钟.小 y 和妹子预计在游乐园里玩 k 分钟.最开始的时候,小 y 和妹子会等概率的随机选择一个娱乐项目开始玩,每玩完一个项目

Loj #6164. 「美团 CodeM 初赛 Round A」数列互质

link : https://loj.ac/problem/6164 莫队傻题,直接容斥做. #include<bits/stdc++.h> #define maxn 100005 #define pb push_back using namespace std; vector<int> g[maxn]; struct ask{ int l,r,K,num,bl; bool operator <(const ask &u)const{ return bl==u.bl?

LibreOJ #6191. 「美团 CodeM 复赛」配对游戏

二次联通门 : LibreOJ #6191. 「美团 CodeM 复赛」配对游戏 /* LibreOJ #6191. 「美团 CodeM 复赛」配对游戏 概率dp 不是很懂为什么这样做... */ #include <cstdio> #include <iostream> const int BUF = 12312312; char Buf[BUF], *buf = Buf; inline void read (int &now) { for (now = 0; !isdi

「题解」「美团 CodeM 资格赛」跳格子

目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞定,最后无奈 \(90pts\) . 然而 \(T2\) 想到很多很奇怪的做法,结果正解在 \(28min\) 之内做出... 结果 \(T3\) 是本人最不擅长的伪期望,直接跳过,啥都没得. 来水一发 \(T1\) 的题解... 题目描述 点这里 考场思路 其实并没有什么十分特别的思路,就是一通乱

[BestCoder Round #3] hdu 4909 String (状压,计数)

String Problem Description You hava a non-empty string which consists of lowercase English letters and may contain at most one '?'. Let's choose non-empty substring G from S (it can be G = S). A substring of a string is a continuous subsequence of th

【状压dp】送餐员

[odevs2800]送餐员 题目描述 Description 有一个送外卖的,他手上有n份订单,他要把n份东西,分别送达n个不同的客户的手上.n个不同的客户分别在1~n个编号的城市中.送外卖的从0号城市出发,然后n个城市都要走一次(一个城市可以走多次),最后还要回到0点(他的单位),请问最短时间是多少.现在已知任意两个城市的直接通路的时间. 输入描述 Input Description 第一行一个正整数n (1<=n<=15) 接下来是一个(n+1)*(n+1)的矩阵,矩阵中的数均为不超过1

LiberOJ #6210. 「美团 CodeM 决赛」tree 树形DP

题目链接:点这里 题解: 需要证明,所求的路径一定是全部权值都为1或者,路径上权值至多有一个为2其余为1且权值2在路径中央. 然后树形DP 设定dp[i][0/1] 以1为根的情况下,以i 节点下子树走分别全1和 走一次2和剩余全走1 的最长链 每遍历一次子树,统计一次答案 下面给出代码 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000&qu

「美团 CodeM 资格赛」试题泛做

LibreOJ真是吼啊! 数码 推个式子,把枚举因数转为枚举倍数.然后就发现它是根号分段的.然后每一段算一下就好了. 1 #include <cstdio> 2 #include <cstring> 3 4 #define R register 5 typedef long long ll; 6 struct Data { 7 ll num[10]; 8 inline void clear() 9 { 10 memset(num, 0, 10 << 3); 11 } 1