【loj6177】「美團 CodeM 初賽 Round B」送外賣2
阿新 • • 發佈:2017-10-12
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