以下是我目前要解决的问题。
有一个名为T的最大值。然后有两个子值A和B,它们是1< = A,B< = T.在每一轮中,你可以选择A或B来添加到你的和。你也可以选择只在其中一轮完成一半的总和。任何一轮都不能超过T.给定无限次数,你可以获得的最大金额是多少。
以下是一个例子: T = 8 A = 5,B = 6
解决方案:我们首先取B,然后取一半的总和3.然后我们加A并得到8.所以最大可能是8.
我提出的迭代思想是:它基本上是一个树结构,你可以保持分支并尝试构建旧的和。我无法找出最大化公式。
是否存在可以快速运行的蛮力解决方案还是有一些优雅的公式?
限制:1&lt; = A,B&lt; = T.T <= 5,000,000。
编辑:当你分割时,你向下舍入总和(即5/2变为2)。
答案 0 :(得分:4)
可以将问题视为具有T + 1
个节点的有向图。想象一下,我们有T + 1
个从0到T
的节点,如果出现以下情况,我们就有从节点x
到节点y
的边缘:
x + A = y
x + B = y
x / 2 = y
因此,为了回答这个问题,我们需要在图表中进行搜索,说明点是节点0
。
我们可以使用breath first search或depth first search来解决问题。
更新:因为我们只能进行一次分割,所以我们必须在图表中添加另一个状态,即isDivided
。但是,解决这个问题的方法并没有改变。
我将用BFS实现演示解决方案,DFS非常相似。
class State{
int node, isDivided;
}
boolean[][]visited = new boolean[2][T + 1];
Queue<State> q = new LinkedList();
q.add(new State(0, 0));//Start at node 0, and haven't use division
visited[0][0] = true;
int result = 0;
while(!q.isEmpty()){
State state = q.deque();
result = max(state.node, result);
if(state.node + A <= T && !visited[state.isDivided][state.node + A]){
q.add(new State(node + A , state.isDivided));
visited[state.isDivided][node + A] = true;
}
if(node + B <= T && !visited[state.isDivided][node + B]){
q.add(new State(node + B, state.isDivided));
visited[state.isDivided][node + B] = true;
}
if(state.isDivided == 0 && !visited[state.isDivided][node/2]){
q.add(new State(node/2, 1));
visited[state.isDivided][node/2] = true;
}
}
return result;
时间复杂度为O(n)
答案 1 :(得分:1)
按照我的理解总结你的问题设置(在你可以除以2的约束下不超过一次):
A
和B
次数(包括每次0)A
和B
目标是获得尽可能大的总和,但要遵守算法任何步骤后总和不超过T
的约束。
这可以在5变量整数程序中巧妙地捕获。五个变量是:
a1
:我们在除以2之前添加A
的次数b1
:我们在除以2之前添加B
的次数s1
:floor((A*a1+B*b1)/2)
,第二步后的总和a2
:我们在除以2 A
的次数
b2
:我们在除以2 B
的次数
最终总和为s1+A*a2+B*b2
,其约束不得超过T
;这就是我们寻求最大化的目标。所有五个决策变量必须是非负整数。
整数编程求解器可以很容易地求解整数程序的最优性。例如,以下是使用R中的lpSolve
包解决问题的方法:
library(lpSolve)
get.vals <- function(A, B, T) {
sol <- lp(direction = "max",
objective.in = c(0, 0, 1, A, B),
const.mat = rbind(c(A, B, 0, 0, 0), c(0, 0, 1, A, B), c(-A, -B, 2, 0, 0), c(-A, -B, 2, 0, 0)),
const.dir = c("<=", "<=", "<=", ">="),
const.rhs = c(T, T, 0, -1),
all.int = TRUE)$solution
print(paste("Add", A, "a total of", sol[1], "times and add", B, "a total of", sol[2], "times for sum", A*sol[1]+B*sol[2]))
print(paste("Divide by 2, yielding value", sol[3]))
print(paste("Add", A, "a total of", sol[4], "times and add", B, "a total of", sol[5], "times for sum", sol[3]+A*sol[4]+B*sol[5]))
}
现在我们可以计算出如何在不超过T的情况下获得总和的高度:
get.vals(5, 6, 8)
# [1] "Add 5 a total of 1 times and add 6 a total of 0 times for sum 5"
# [1] "Divide by 2, yielding value 2"
# [1] "Add 5 a total of 0 times and add 6 a total of 1 times for sum 8"
get.vals(17, 46, 5000000)
# [1] "Add 17 a total of 93 times and add 46 a total of 0 times for sum 1581"
# [1] "Divide by 2, yielding value 790"
# [1] "Add 17 a total of 294063 times and add 46 a total of 3 times for sum 4999999"
答案 2 :(得分:0)
我们可以用这种方式描述问题:
f(A , B) = (A * n + B * m) / 2 + (A * x + B * y)
= A * (n * 0.5 + x) + B * (m * 0.5 + y) =
= A * p + B * q
find N: N = f(A , B) and N <= T such that no M: M > N satisfying
the condition exists.
没有任何两个除法的情况可以很容易地用n = m = 0
来表示,因此也被f
覆盖。
n
和y
可以是匹配p = n * 0.5 + y
的任意值(q
和相关值相同)。请注意,f
中有多种有效的解决方案。
T >= A * p + B * q
r = p * 2, s = q * 2
find integral numbers r, s satisfying the condition
T >= A * r / 2 + B * s / 2
simplify:
T * 2 / B >= A / B * r + s
因此我们知道:
(T / B * 2) mod 1 - (A / B * r) mod 1 is minimal and >= 0 for the optimal solution
T * 2 / A >= r >= 0 are the upper and lower bounds for r
(A / B * r) mod 1 = 0, if r = B / gcd(A , B) * n, where n is an integral number
使用二进制搜索现在使用这些约束查找r变得非常简单。可能有更有效的方法,但O(log B)
应该为此目的:
Apply a simple binary-search to find the matching value
in the range [0 , min(T * 2 / A , B / gcd(A , B))
可以轻松地为任何相应的s
r
s = roundDown(T * 2 / B - A * r / B)
E.g:
A = 5
B = 6
T = 8
gcd(A , B) = 1
search-range = [0 , 6)
(T / B * 2) mod 1 = 4 / 6
(A / B * r) mod 1 =
r = 3: 3 / 6 => too small --> decrease r
r = 1: 5 / 6 => too great --> increase r
r = 2: 4 / 6 => optimal solution, r is found
r = 2
s = roundDown(T * 2 / B - A * r / B) = roundDown(3.2 - 1.66) = 1
p = r / 2 = 1 = 1 + 0 = 2 * 0.5 --> n = 1 y = 0 or n = 2 y = 0
q = s / 2 = 0.5 --> n = 0.5 y = 0
8 >= 5 * 1 + 5 * 0.5 * 0 + 0 * 6 + 1 * 0.5 * 6 = 5 + 3
= 5 * 0 + 5 * 0.5 * 2 + 0 * 6 + 1 * 0.5 * 6 = 5 + 3
此方法的优点:我们可以在O(log B)
中找到所有解决方案:
如果找到r的值,则所有其他值r&#39;匹配约束如下:r' = r + B / gcd(A , B) * n
。 A
和B
可以通过此方法进行交换,从而可以使用较小的输入值B
进一步优化。
在算法中将变量除以2时的值的舍入应该只会导致小问题,这很容易修复。
答案 3 :(得分:0)
我会使用一些数学方法。
恢复:
你应该能够用A,B,T来计算最大值,而无需迭代(仅用于获得A / B HCD),对于T,而不是小。
如果A或B是奇数,则max = T(有保留,我不确定你是否永远不会超过T:见下文)。
如果A 和 B为偶数,则将C作为最高公因子。然后max = round(T / C * 2)* C / 2 = C / 2的最高倍数低于或等于T
一些解释:
使用规则:A p + B q(不除以2)
1假设A和B是素数,那么你可以在小孩之后得到你想要的每个整数。然后max = T
示例:A = 11,B = 17
2如果A = C x,并且B = C y,x,y素数在一起(如10和21),则可以得到每个C倍数,然后max = C下面的最大倍数T:圆形(T / C)* C
示例:A = 33,B = 51(C = 3)
使用规则:你可以除以2
3 - 如果C是偶数(即A和B可以除以2):max = C / 2的倍数低于T:round(T / C * 2)* C / 2
示例:A = 22,B = 34(C = 2)
4 - 否则,你必须找到A,B,圆(A / 2),圆(B / 2)的最大dividor(最高公因子),称之为D,max = D以下D的最大倍数:圆(T / D)* D. 由于A和圆(A / 2)是素数(B和圆(B / 2)的同义词),然后你可以得到max = T,如案例1 - 警告:我不确定你是否永远不会去过去T.检查