• 【loj6177】「美团 CodeM 初赛 Round B」送外卖2 Floyd+状压dp


    题目描述

    一张$n$个点$m$条边的有向图,通过每条边需要消耗时间,初始为$0$时刻,可以在某个点停留。有$q$个任务,每个任务要求在$l_i$或以后时刻到$s_i$接受任务,并在$r_i$或以前时刻到$t_i$完成任务。同一时刻可以接受多个任务。问:最多能完成多少任务。

    输入

    第一行,三个正整数$n$、$m$、$q$;

    接下来$m$行,每行三个正整数$u_i$、$v_i$、$c_i$,表示有一条从$u_i$到$v_i$,耗时$c_i$的边。

    接下来$q$行,每行四个正整数$s_i$、$t_i$、$l_i$、$r_i$,描述一个任务。

    输出

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

    样例输入

    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


    题解

    Floyd+状压dp

    比赛时现场切的题,今天想到,于是写了题解。

    显然两点之间一定是走最短路的,于是先使用Floyd预处理出两点之间的最短路。

    然后考虑状压。由于一个任务有三种状态:未接受、接受但未完成、已完成。于是可以设三进制状态,表示每个任务的进程。

    而时间范围过大无法设为状态,于是需要用dp值表示。考虑到可以逗留,因此时间越短,答案不会更劣。

    于是设$f[i][j]$表示任务进程的状态为$i$,当前在点$j$的最短时间。那么枚举每一个任务,考虑其转移方式即可。

    注意这里只需要考虑第二个点是任务的起点/终点的情况,因为保证了走的是最短路,中间的点停留没有意义(所以本题n和m可以出到3W左右)。

    最后判断哪个状态合法,统计该状态能够做的任务个数即可。

    时间复杂度$O(3^q·q^2)$。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int dis[25][25] , f[60000][25] , base[15] , qx[15] , qy[15] , ql[15] , qr[15];
    int main()
    {
    	int n , m , q , i , j , k , x , y , z , ans = 0 , tmp;
    	scanf("%d%d%d" , &n , &m , &q);
    	memset(dis , 0x3f , sizeof(dis));
    	for(i = 1 ; i <= n ; i ++ ) dis[i][i] = 0;
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &x , &y , &z) , dis[x][y] = min(dis[x][y] , z);
    	for(k = 1 ; k <= n ; k ++ )
    		for(i = 1 ; i <= n ; i ++ )
    			for(j = 1 ; j <= n ; j ++ )
    				dis[i][j] = min(dis[i][j] , dis[i][k] + dis[k][j]);
    	base[0] = 1;
    	for(i = 1 ; i <= q ; i ++ ) scanf("%d%d%d%d" , &qx[i] , &qy[i] , &ql[i] , &qr[i]) , base[i] = base[i - 1] * 3;
    	memset(f , 0x3f , sizeof(f)) , f[0][1] = 0;
    	for(i = 1 ; i < base[q] ; i ++ )
    	{
    		for(j = 1 ; j <= n ; j ++ )
    		{
    			for(k = 1 ; k <= q ; k ++ )
    			{
    				if(i % base[k] / base[k - 1] == 1)
    					f[i][qx[k]] = min(f[i][qx[k]] , max(f[i - base[k - 1]][j] + dis[j][qx[k]] , ql[k]));
    				else if(i % base[k] / base[k - 1] == 2 && f[i - base[k - 1]][j] + dis[j][qy[k]] <= qr[k])
    					f[i][qy[k]] = min(f[i][qy[k]] , f[i - base[k - 1]][j] + dis[j][qy[k]]);
    			}
    		}
    	}
    	for(i = 0 ; i < base[q] ; i ++ )
    	{
    		for(j = 1 ; j <= n ; j ++ )
    		{
    			if(f[i][j] != 0x3f3f3f3f)
    			{
    				for(tmp = k = 0 ; k < q ; k ++ )
    					if(i % base[k + 1] / base[k] == 2)
    						tmp ++ ;
    				ans = max(ans , tmp);
    			}
    		}
    	}
    	printf("%d
    " , ans);
    	return 0;
    }
    
  • 相关阅读:
    设置Fedora core 6中yum光盘源 去除无收集不克不及翻开软件包治理的标题效果
    从头放置Windows后Ubuntu 8.04启动的恢复
    _desktop.ini“维金(Worm.Viking.m)”的病毒?
    理顺 JavaScript (17) 函数
    理顺 JavaScript (15) 类的继承手段: prototype
    UniCode 速查表
    理顺 JavaScript (16) 使用 prototype
    一句话判断网络是否联通
    给 Edit 两个可选值 回复 "delphi学习中" 的问题
    理顺 JavaScript (20) String 中的正则表达式函数
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7657769.html
Copyright © 2020-2023  润新知