【题解】摆渡车

题目描述

  有n名同学要乘坐摆渡车从人大附中前往人民大学,第i位同学在第ti分钟去等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费m分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。

  凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?

  注意:摆渡车回到人大附中后可以即刻出发。

 

输入格式

  第一行包含两个正整数n,m,以一个空格分开,分别代表等车人数和摆渡车往返一趟的时间。

  第二行包含n个正整数,相邻两数之间以一个空格分隔,第i个非负整数ti代表第i个同学到达车站的时刻。

输出格式

  一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。

输入样例一

5 1

3 4 4 3 5

输出样例一

0

输入样例二

5 5

11 13 1 5 5

输出样例二

4

样例说明

  样例一说明:

  同学1和同学4在第3分钟开始等车,等待0分钟,在第3分钟乘坐摆渡车出发。摆渡车在第4分钟回到人大附中。

  同学2和同学3在第4分钟开始等车,等待0分钟,在第4分钟乘坐摆渡车出发。摆渡车在第5分钟回到人大附中。

  同学5在第5分钟开始等车,等待0分钟,在第5分钟乘坐摆渡车出发。自此所有同学都被送到人民大学。总等待时间为 0。

  样例二说明:

  同学3在第1分钟开始等车,等待0分钟,在第1分钟乘坐摆渡车出发。摆渡车在第6分钟回到人大附中。

  同学4和同学5在第5分钟开始等车,等待1分钟,在第6分钟乘坐摆渡车出发。摆渡车在第11分钟回到人大附中。

  同学1在第11分钟开始等车,等待2分钟;同学2在第13分钟开始等车,等待0分钟。他/她们在第13分钟乘坐摆渡车出发。自此所有同学都被送到人民大学。

  总等待时间为4。可以证明,没有总等待时间小于4的方案。

数据规模

  对于10%的数据,n≤10,m=1,0≤ti≤100。

  对于30%的数据,n≤20,m≤2,0≤ti≤100。

  对于50%的数据,n≤500,m≤100,0≤ti≤10000

  另有20%的数据,n≤500,im≤10,0≤ti≤1000000

  对于100%的数据,n≤500,m≤100,0≤ti≤4000000

题解

  话说当年考场上做这道题时感觉毫无头绪,考完noip后也一直把这题弃着,直到放假了有时间做,发现也就这么一回事。

  洛谷上评分是蓝题,个人感觉顶多绿题。。。


  回到正题,观察这一题的数据范围,可以看出$n,m$都很小,那我们可以考虑从这方面进行dp。

  首先我们按照升序对$a[i]$进行排序(由于个人习惯,这里这里及下文用$a[i]$表示$t[i]$)。

  那我们设$dp[i][j]$为第$i$个同学在$a[i]+j$的时间点出发时,第$1$到第$i$个同学的等待时间之和的最小值,可以想到$0 \leqslant j < m$。(如果$m \leqslant j$的话,那正在等待的同学实际上可以在$a[i]+j-m$的时间点出发,不需要继续等下去故$0 \leqslant j < m$)

  容易想到,$dp[i][j]=min \left \{dp[ii][jj]+wait(i, j, ii) \right \}$($0 \leqslant ii < i$),其中$wait(i, j, ii)$为第$ii$到第$i$个同学的等待时间之和。

  根据上文对$dp[i][j]$的定义,可以得到:$a[i] + j < a[i + 1]$同理$a[ii] + jj < a[ii + 1]$。同时,根据这个方程还可以得到$a[ii] + jj + m \leqslant a[i] + j$。

  同时我们也可以想到,如果设$s[i] = \sum_{j = 1}^{i} a[j]$,那么$wait(i, j, ii) = (a[i] + j) \times (i - ii) - (s[i] - s[ii])$

  此时我们可以得到一个时间复杂度为$O(n^{2}m^{2})$的程序,是可以拿70pts的。

#include <iostream>
#include <cstring>
#include <algorithm>

#define MAX_N (500 + 5)
#define MAX_M (100 + 5)

#define INF 0x7f7f7f7f

#define WAIT(i, j, ii) ((a[(i)] + j) * ((i) - (ii)) - (s[(i)] - s[(ii)]))

using namespace std;

int n, m;
int a[MAX_N], s[MAX_N];
int dp[MAX_N][MAX_M];
int ans = INF;

int main()
{
    cin >> n >> m;
    for(register int i = 1; i <= n; ++i)
    {
        cin >> a[i];
    }
    sort(a + 1, a + n + 1);
    for(register int i = 1; i <= n; ++i)
    {
        s[i] = s[i - 1] + a[i];
    }
    memset(dp, 0x7f, sizeof dp);
    a[0] = a[1] - m;
    a[n + 1] = INF;
    for(register int j = 0; j < m; ++j)
    {
        dp[0][j] = 0;
    }
    int tmp;
    for(register int i = 1; i <= n; ++i)
    {
        for(register int j = 0; j < m && a[i] + j < a[i + 1]; ++j)
        {
            for(register int ii = 0; ii < i; ++ii)
            {
                tmp = INF;
                for(register int jj = 0; jj < m && a[ii] + jj < a[ii + 1] && a[ii] + jj + m <= a[i] + j; ++jj)
                {
                    tmp = min(tmp, dp[ii][jj]);
                } 
                if(tmp < INF) dp[i][j] = min(dp[i][j], tmp + WAIT(i, j, ii));
            }
        }
    }
    for(register int j = 0; j < m; ++j)
    {
        ans = min(ans, dp[n][j]);
    }
    cout << ans;
    return 0;
} 
参考程序(70pts)

  观察上面的方程,显然最容易优化的就是求“$min \left \{dp[ii][jj]+wait(i, j, ii) \right \}$"的部分,我们可以想一下是否可以开一个数组维护这些最小值。

  事实上,这是可行的。

  由于这个方程中,始终是$0

猜你喜欢

转载自www.cnblogs.com/kcn999/p/10800160.html
今日推荐