前段时间,我正在研究编程问题(CCC)。我在过去的比赛中也遇到过类似的问题所以我决定问一下这个问题。问题基本上就是这个。
给你n个人和p个馅饼。
人们站成一排。您必须在其中分发p个馅饼。你按顺序进行,每个人必须至少收到与他们之前一样多的棋子。每个人必须至少收到一块馅饼,不得留下任何馅饼。
您必须返回分发饼图的可能方式的数量。
我设法创建了以下递归解决方案,但以下输入需要太长时间(超过5秒):
120件,20人 - > 97132873
250件,130人 - > 1844349560
我的解决方案:
import java.io.*;
public class Main
{
int pieces, people;
int combinations = 0;
public void calculate (int person, int piecesLeft, int prev)
{
if (person == people)
{
if (piecesLeft == 0)
combinations++;
}
else
{
for (int x = prev ; (x * (people - person)) <= piecesLeft ; x++)
{
calculate (person + 1, piecesLeft - x, x);
}
}
}
public static void main (String[] args) throws Exception
{
Main m = new Main ();
BufferedReader in = new BufferedReader (new InputStreamReader (System.in));
//m.pieces = Integer.parseInt (in.readLine ());
//m.people = Integer.parseInt (in.readLine ());
m.pieces=250;
m.people=130;
if (m.people == m.pieces)
System.out.println (1);
else if (m.people == 1)
System.out.println (1);
else
{
m.calculate (0, m.pieces, 1);
System.out.println (m.combinations);
}
}
}
我从非官方的解决方案中找到了以下python解决方案,根据我的理解,它基本上创建了一个已经遇到的值的数组。
visited = []
def pi(n,k,min):
if visited [n][k][min] == 0:
if n == k:
visited[n][k][min] = 1
elif k == 1:
visited[n][k][min] = 1
else:
t = 0
for i in range (min, (n / k)+1):
t = t + pi(n-i, k-1, i)
visited[n][k][min] = t
return visited[n][k][min]
file = open("j5.10.in", "r")
n = int(file.readline())
k = int(file.readline())
for i in range(n+1):
x = []
for j in range(k+1):
t = []
for kk in range(n+1):
t.append (0)
x.append(t)
visited.append(x)
print pi(n,k,1)
我想要做的是从其中任何一个中做出迭代解决方案,但我不知道该怎么做。根据我的理解,可能没有巨大的速度差异,但是对于更大的情况,它将允许我避免堆栈溢出。
答案 0 :(得分:1)
第二个解决方案是memoized ... visited
数组记录已经计算过的值。将memoized递归转换为(某种)迭代解决方案的技巧是循环通过较小的情况来填充memo数组。您可以丢弃较小案例的结果(无论如何它们都将存储在备忘录数组中)。然后,当你最终计算出你想要的那个时,它将立即使用备忘录数组,无需额外的计算。
如果您真的想从头开始构建迭代解决方案,那么您必须弄清楚以前需要存储的案例才能构建下一个案例。例如,要计算阶乘,使用循环,您只需要在内存中存储一个值。在硬币面额为1美分,5美分和10美分的变更问题中,您只需要保存前十个项目来构建下一个项目。在某些情况下,您需要知道所有先前的值才能构建下一个值。一旦你知道了,内存结构应该清楚,那么程序逻辑就会变得清晰。