Java 如何将此问题分解为子问题并使用动态规划?
从2019年开始,我一直在处理一个旧的竞赛问题: 您正计划参观N个旅游景点。景点编号从1到N,必须按此顺序参观。你每天最多可以参观K个景点,并希望行程安排尽可能少的天数 在这些限制条件下,您希望在每天游览的景点之间找到一个平衡的时间表。准确地说,我们给吸引力i分配了一个分数ai。给出时间表后,每天的分数等于当天参观的所有景点的最高分数。最后,将每天的分数相加,得出日程安排的总分。使用尽可能少的天数,时间表的最大可能总分是多少 很明显,这是一个动态规划类型的问题,我可以看到它是如何产生的,但我似乎无法理解如何将它分解为子问题,以及每个子问题如何相互关联,特别是当有两个变量N和K时 我提出了一个递归蛮力算法,该算法适用于较小的输入,但在输入过大时失败:Java 如何将此问题分解为子问题并使用动态规划?,java,algorithm,dynamic-programming,Java,Algorithm,Dynamic Programming,从2019年开始,我一直在处理一个旧的竞赛问题: 您正计划参观N个旅游景点。景点编号从1到N,必须按此顺序参观。你每天最多可以参观K个景点,并希望行程安排尽可能少的天数 在这些限制条件下,您希望在每天游览的景点之间找到一个平衡的时间表。准确地说,我们给吸引力i分配了一个分数ai。给出时间表后,每天的分数等于当天参观的所有景点的最高分数。最后,将每天的分数相加,得出日程安排的总分。使用尽可能少的天数,时间表的最大可能总分是多少 很明显,这是一个动态规划类型的问题,我可以看到它是如何产生的,但我似
int daysNeeded = (int) Math.ceil((double) N / K);
// n - index of current attraction being visited
// d - days used up
public static long solve(int n, int d) {
if (d == daysNeeded) { // Base case, stop once we reach the min days required
if (n == N) // If we visited all attractions, count this answer
return 0;
else // If we didn't visit all attractions, don't count this answer
return Integer.MIN_VALUE;
}
long result = 0;
// Try to visit attractions up to K
//
// i + 1 is the number of attractions to visit in this day
for (int i = 0; i < K; i++) {
if (n + i >= N)
break;
long highestScore = attractions[n];
// Find the attraction from [n + j ... n + i] with the highest score
for (int j = 1; j <= i; j++) {
highestScore = Math.max(highestScore, attractions[n + j]);
}
long next = highestScore + solve(n + i + 1, d + 1);
// Find the attraction with the highest score out of all attractions from 0 to i
result = Math.max(result, next);
}
return result;
}
int daysneed=(int)Math.ceil((double)N/K);
//n-当前被访问景点的指数
//d天用完了
公共静态长解算(int n,int d){
如果(d==daysNeeded){//基本情况,在达到所需的最小天数后停止
如果(n==n)//如果我们参观了所有景点,数一数这个答案
返回0;
否则//如果我们没有参观所有的景点,不要计算这个答案
返回Integer.MIN_值;
}
长结果=0;
//尽量去游览景点到K
//
//i+1是指当天游览的景点数量
for(int i=0;i=n)
打破
长期最高分数=景点[n];
//从得分最高的[n+j…n+i]中找到吸引力
对于(int j=1;j我将尝试以递推关系的形式给出解。
设m为参观所有景点的天数,设P[m][N]为您在m天内参观N个景点所获得的最佳值。我们还不知道P,但我们将对此进行推理
p[m][N]=max{i到k}(p[m-1][N-i]+max{l=0到i-1}(a[l]))
例如,如果您在最后一天只游览了最后两个景点,就得到了最佳分数,那么当天的分数为max(a[N],a[N-1]),总(最佳)分数为
p[m][N]=最大(a[N],a[N-1])+在m-1天内游览N-2个景点的最佳分数
这与上面的公式完全相同
p[m][N]=max(a[N],a[N-1]+p[m-1][N-2]
请注意,i>N/k(m-1)有一个限制,因为如果您在最后一天没有参观足够的景点,那么剩下的几天可能不足以参观其余的景点。让我们从每天分配k
景点开始,最后一天除外,它的长度为m=N mod k
。例如:
5 3
2 5 7 1 4
2 5 7|1 4 (M = 5 mod 3 = 2)
请注意,我们不能延长任何K
长度天数,也不能缩短任何天数,除非我们先延长较小的M
长度天数。请注意,我们可以延长的最大金额等于K-M=K-(N mod K)
现在,让dp[d][m]
代表天数1…d
的最佳分数,当天数d+1
已扩展到m
景点开始的d
第天。调用所需天数d=ceil(N/K)
。然后:
dp[1][m] = max(attractions[0..k-m-1])
dp[D][m] = max(attractions[i-m..j]) + dp[D-1][m]
dp[d][m] = max(attractions[i-l..j-m]) + dp[d-1][l]
where (i, j) mark the starting dth day
and 0 ≤ l ≤ m
答案将是dp[D][m]
中最好的一个
我们可以将O(1)
中相关最大值的计算折叠到例程中:从左到右预处理O(n)
中每个起始部分(表示天数)的前缀最大值。对于max(吸引力[i-l..j-m])的每个循环
,从前缀max中j-m
处提供的最大值开始,然后通过将当前值与每个景点[i-l]
进行比较来更新最大值,因为l
是递增的
总体复杂性似乎是O(ceil(N/K)*(K-(N mod K))^2)
在时间方面,我们可以做得更好,通过观察随着m
的增加,我们可以跳过l
上的迭代,如果起始最大值没有变化,或者之前选择了一个大于起始最大值的最大值(这意味着它来自i
)在这种情况下,我们只需要考虑新的<代码> L>代码>,它比我们以前检查的要大一点。我们可以依靠右到左前缀MAX加上我们的左到右前缀MAX,在<代码> O(1)< /代码>中获得这个新的MAX。
在我们的简单示例中,我们有:
2 5 7 1 4
dp[1][0] = max(2, 5, 7) = 7
dp[1][1] = max(2, 5) = 5
dp[2][0] = max(1, 4) + dp[1][0] = 11
dp[2][1] = max(7, 1, 4) + dp[1][1] = 12
如果你缓存solve(n,d)
你就有了一个动态规划解决方案。我不明白“daysNeeded”(n除以K)有什么意义?这看起来像一个优化问题,但它有两个目标函数,并且不清楚哪一个被赋予优先级-最大分数还是最小天数?您的解决方案假设天数最多为N/K(类似于背包容量)但是我在问题陈述中看不到任何关于这个约束的东西啊,而且你假设天数应该正好是N/K,但是如果天数更多,可能会有更好的分数。无论如何,问题陈述是模糊的,需要修改revised@mangusta如果我们可以有任意天数,最大可实现分数为每天只参观一个景点。天数最多不是ceil(n/k),而是ceil(n/k)我想,任何一天我们都可以缩小K。假设K是3,N是8,那么有效分布可以是第一天3,第二天2,第三天3day@ShubhamAgrawal我同意,我的描述也是。哪一部分让你不这么认为?