1. 程式人生 > >【NOIP2018】【洛谷P5017】擺渡車【DP】

【NOIP2018】【洛谷P5017】擺渡車【DP】

題目大意:

題目連結:https://www.luogu.org/problemnew/show/P5017
n n 個人分別在 t 1 t

n t_1\sim t_n 的時間到達,一輛擺渡車要把這些人送到另外一個地方,擺渡車來回一次要 m m 的時間單位。求把這些人都送到的最短時間。


思路:

肯定可以先把 t

t 排序。
我們知道,一個人到達後發車只會有兩種情況:

  1. 擺渡車在他到達之前就到了。此時可以直接發車。
  2. 擺渡車在他到達後 k k 分鐘才到。此時要等 k
    k
    分鐘才能發車。

可以先預處理出 s [ i ] [ j ] s[i][j] ,表示第 i i 個人到第 j j 個人做同一輛車的等待時間。那麼就有
s [ i ] [ j ] = j = 1 n i = 1 j 1 k = i j 1 t j t k s[i][j]=\sum^{n}_{j=1}\sum^{j-1}_{i=1}\sum^{j-1}_{k=i}t_j-t_k
那麼我們就設 f [ i ] [ j ] f[i][j] 表示在 i i 個人到達後發車,第 i i 個人等了 j j 分鐘時的最小等待時間。
那麼肯定要列舉 j j ,表示前 j j 個人已經送到了目的地。
那麼如果第 i i 個人到達時,擺渡車已經回來了,那麼就可以直接發車(即第 i i 個人的等待時間為 0 0 )。此時就有
f [ i ] [ 0 ] = m i n ( f [ i ] [ 0 ] , f [ j ] [ k ] + s [ j + 1 ] [ i ] ) f[i][0]=min(f[i][0],f[j][k]+s[j+1][i])
其中 k k 表示列舉的第 j j 個人等待的時間。
那麼如果第 i i 個人到達後襬渡車沒有回來,那麼第 i i 個人等待的時間就是
w = t j + k + m t i w=t_j+k+m-t_i
其中 t j + k + m t_j+k+m 是擺渡車回到的時間。
那麼就有
f [ i ] [ w ] = m i n ( f [ i ] [ w ] , f [ j ] [ k ] + s [ j + 1 ] [ i ] + ( i j ) w ) f[i][w]=min(f[i][w],f[j][k]+s[j+1][i]+(i-j)*w)
答案就是 m i n ( f [ n ] [ i ] ) ( i = 0 m ) min(f[n][i])(i=0\sim m)
時間複雜度 O ( n 2 m ) O(n^2m) ,足夠過掉本題。


程式碼:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int N=510;
const int M=210;
const int Inf=2e9;
int n,m,ans,w,t[N],f[N][M],s[N][N];

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&t[i]);
	sort(t+1,t+1+n);
	for (int j=1;j<=n;j++)
		for (int i=1;i<j;i++)
			for (int k=i;k<j;k++)
				s[i][j]+=t[j]-t[k];
	memset(f,0x3f3f3f3f,sizeof(f));
	t[0]=-Inf;
	for (int i=0;i<=m;i++)  //初始化
	{
		f[0][i]=0;
		f[1][i]=i;
	}
	for (int i=2;i<=n;i++)
		for (int j=0;j<i;j++)
			for (int k=0;k<=m;k++)
			{
				w=t[j]+k+m-t[i];
				if (w>0)
					f[i][w]=min(f[i][w],f[j][k]+s[j+1][i]+(i-j)*w);
				else
					f[i][0]=min(f[i][0],f[j][k]+s[j+1][i]);
			}
	ans=Inf;
	for (int i=0;i<=m;i++)
		ans=min(ans,f[n][i]);
	printf("%d\n",ans);
	return 0;
}