链接:https://ac.nowcoder.com/acm/contest/6630/C
来源:牛客网
牛牛公司老板让牛牛负责m个冰激凌的运输。运输车的冷库只够装n个冰激凌,一次运输需要t分钟,返回也需要t分钟。每个冰激凌制作好有一个时间。牛牛想知道最短运输完所有冰激凌的时间,以及在时间最短的情况下最少运输次数。
示例1
输入
2,3,10,[10,30,40]
输出
[50,2]
备注:
(1≤n,m,t≤2000)
思路:
动态规划,本题的关键在于如何将问题抽象到动态规划模型之中,即如何利用动态规划的解题思路来理解题目。
1. 确定状态。对于题目来说,需要求两个最优的值,而比较迷惑的是,对于最少运输次数而言,与运送时间最短的关系并不相关。换句话说,对于取得最短运送时间的运送次数,一定是所有运送方案中运送次数最少的。证明如下:对于n号冰激凌来说,要完成它的运送至少是在n+t的时间完成,那么在这之前,不可能存在一种方案能够使得这个时间提前,也就是说在这之前的最少的方案就是贪心的运送方案。因此使用两个dp数组:dp[]和ctimes[]分别记录最短时间和最少次数。
2. 写到这儿发现并不需要动态规划,n个一车拉就完事了;
3. 但是还是把动态规划的思路写完,确定状态转移方程,对于最短时间来说,dp[i]应该是等于,dp[i] = min(dp[i],max(dp[j]+t,c[i])+t);怎么理解呢,就是在之前的i之前的n个(存在一车一起拉走的可能性),n个他们送完之后再送i和他们等着i一起送这两者之间较大的,这n个中的最小值。
4. 确认初始值。dp[0]原本应该等于0,但是在本题中,根据递推公式来说,dp[1]如果需要等于c[1]+t,那么dp[0]+t就一定需要小于c[1]。所以dp[0]=-t(理论上小于-t也可以)。ctimes[0]=0,ctimes[1]=1。
动态规划方法
class Solution {
public:
/**
* 两个数表示答案
* @param n int整型 一次运输的冰激凌数量
* @param m int整型 总冰激凌数
* @param t int整型 一次运输的时间
* @param c int整型一维数组 表示每个冰激凌制作好时间<1e4
* @param cLen int c数组长度
* @return int整型vector
*/
vector<int> icecream(int n, int m, int t, int* c, int cLen) {
vector<int> dp(2020,INT_MAX);
vector<int> ctimes(2020,INT_MAX);
// write code here
sort(c,c+cLen);
for(int i=cLen;i>=1;i--)
{
c[i] = c[i-1];
}
c[0]=0;
dp[0] = -t;
ctimes[0]=0;
ctimes[1]=1;
for(int i = 1;i<=m;i++)
{
for(int j = max(0,i-n);j<i;j++)
{
dp[i] = min(dp[i],max(dp[j]+t,c[i])+t);
ctimes[i] = min(ctimes[j]+1,ctimes[i]);
}
}
return {dp[m],ctimes[m]};
}
};
贪心方法:
class Solution {
public:
/**
* 两个数表示答案
* @param n int整型 一次运输的冰激凌数量
* @param m int整型 总冰激凌数
* @param t int整型 一次运输的时间
* @param c int整型一维数组 表示每个冰激凌制作好时间<1e4
* @param cLen int c数组长度
* @return int整型vector
*/
vector<int> icecream(int n, int m, int t, int* c, int cLen) {
// write code here
sort(c,c+cLen);
int ans = -t;
int start = (m%n)?(m%n):n;
for(int i = start-1;i<m;i+=n)
{
ans+=t;
ans = max(c[i],ans);
ans+=t;
}
return {ans,m/n+((m%n)!=0)};
}
};