1. 程式人生 > >【loj6177】「美團 CodeM 初賽 Round B」送外賣2

【loj6177】「美團 CodeM 初賽 Round B」送外賣2

ace using print font names -s 統計 ase round

題目描述

一張$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\n" , ans);
	return 0;
}

【loj6177】「美團 CodeM 初賽 Round B」送外賣2