Grazing on the Run 題解
阿新 • • 發佈:2020-08-09
【題目大意】
大致題意就是,你的初始座標為\(x\),你要去數軸上的\(n\)個點,問你到達所有點的時間總和最小是多少。
直接貪心肯定不行,所以考慮\(DP\)
先把座標離散(也就是預處理兩點距離\(dis[i][j]=abs(a[i]−a[j])\))
接下來考慮如何dp。
關注到一個性質,如果到目前為止,奶牛吃過最左的草堆編號為\(l\),吃過最右的草堆編號為\(r\),則如果奶牛不是傻它肯定把\([l,r]\)的草堆都吃過了,因為它吃草速度是瞬時的,都經過了肯定要嫖一口。
那很明顯應該是個區間dp了。
不難定義出狀態\(f[0/1][i][j]\)表示已經吃完\([i,j]\)的草了,且現在在左端\(i(0)\)
轉移根據意義模擬一下就好了,假如我現在從區間的某端\(k\)轉移到某點\(l\),則花去時間為\(dis[k][l]\),在這個時間內除了區間\([i,j]\),其他所有草堆的腐敗值都增加了\(1\)。
具體轉移順序可以打個記搜。也可以直接迴圈轉移——列舉區間長度,再列舉左端點。然後對於這道題內部再分類討論一下處於左右端位置即可。時間複雜度為\(O(N^2)\)。
#include <bits/stdc++.h> using namespace std ; const int N = 1005 , INF = 0x3f3f3f3f ; int n , s , st ; int p[ N ] ; int f[ N ][ N ][ 2 ] ; int dis[ N ][ N ] ; signed main () { scanf ( "%d%d" , &n , &s ) ; for ( int i = 1 ; i <= n ; i ++ ) scanf ( "%d" , &p[ i ] ) ; p[ ++ n ] = s ; sort ( p + 1 , p + 1 + n ) ; for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= n ; j ++ ) dis[ i ][ j ] = dis[ j ][ i ] = abs ( p[ i ] - p[ j ] ) ; st = lower_bound ( p + 1 , p + 1 + n , s ) - p ; memset ( f , 0x3f , sizeof ( f ) ) ; f[ st ][ st ][ 0 ] = f[ st ][ st ][ 1 ] = 0 ; for ( int i = 1 ; i <= n ; i ++ ) { for ( int l = 1 ; l + i - 1 <= n ; l ++ ) { int r = i + l - 1 ; if ( f[ l ][ r ][ 0 ] < INF ) { if ( l > 1 ) f[ l - 1 ][ r ][ 0 ] = min ( f[ l - 1 ][ r ][ 0 ] , f[ l ][ r ][ 0 ] + dis[ l ][ l - 1 ] * ( n - i ) ) ; if ( r < n ) f[ l ][ r + 1 ][ 1 ] = min ( f[ l ][ r + 1 ][ 1 ] , f[ l ][ r ][ 0 ] + dis[ l ][ r + 1 ] * ( n - i ) ) ; } if ( f[ l ][ r ][ 1 ] < INF ) { if ( r < n ) f[ l ][ r + 1 ][ 1 ] = min ( f[ l ][ r + 1 ][ 1 ] , f[ l ][ r ][ 1 ] + dis[ r ][ r + 1 ] * ( n - i ) ) ; if ( l > 1 ) f[ l - 1 ][ r ][ 0 ] = min ( f[ l - 1 ][ r ][ 0 ] , f[ l ][ r ][ 1 ] + dis[ r ][ l - 1 ] * ( n - i ) ) ; } } } printf ( "%d\n" , min ( f[ 1 ][ n ][ 0 ] , f[ 1 ][ n ][ 1 ] ) ) ; return 0 ; }