问题分析:
假定N = 1, 则可能的整数集这能为{1}, 所以个数为1。
假定M = 1,N > 1, 则不可能有合适的整数集,所以个数为0。
假定M > N, 则结果集的个数和M = N的一样多, 因为不可能出现比N大的数。
假定M <= N, M > 1, N > 1, 此时我们有两种情况,结果集中包括M, 或者不包括。最终的数量为这两种情况的数量之和。
假定我们用F(N,M)来表示结果集的数量。则有如下表达式:
F(1,M) = 1; M为任意值.
F(N,1) = 0; N > 1.
F(N,M) = F(N,N); M > N.
F(N,M) = F(N - M,M - 1) + F(N,M - 1);M <= N, M > 1, N > 1.
一般有两种方式来解决这种问题,递归和动态规划。
递归方式:
public static int GetPartitionCount2(int number, int maxElement)
{
if (number == 1)
return 1;
if (maxElement == 1)
return 0;
if (maxElement < number)
return GetPartitionCount2(number - maxElement, maxElement - 1) + GetPartitionCount2(number, maxElement - 1);
else
return 1 + GetPartitionCount2(number, number - 1);
}
动态规划:
public static int GetPartitionCount1(int number, int maxElement)
{
int[,] calculateTable = new int[number + 1, maxElement + 1];
calculateTable[1, 1] = 1;
for (int i = 2; i < number + 1; i++)
{
calculateTable[i, 1] = 0;
}
for (int i = 1; i < maxElement + 1; i++)
{
calculateTable[1, i] = 1;
}
for (int i = 2; i < maxElement + 1; i++)
{
for (int j = 2; j < number + 1; j++)
{
if(i<j)
calculateTable[j, i] = calculateTable[j - i, i - 1] + calculateTable[j, i - 1];
else if(i>=j)
calculateTable[j, i] = 1 + calculateTable[j, j - 1];
}
}
return calculateTable[number, maxElement];
}
就递归方式而言,最大的不好之处就是递归次数太多,做了太多的冗余计算。
(N,M) 所需时间(ms)
100,100 250
120,120 1281.3
140,140 6297
160,160 27484.7
180,180 110844.5
200,200 398417.4
相对而言,动态规划效率要高很多,不是一个数量级的。
(N,M) 所需时间(ms)
1000,1000 46
2000,2000 187.5
3000,3000 593.8
4000,4000 1719.0
5000,5000 3172.3
6000,6000 5657.0
7000,7000 9345.1
8000,8000 13220.6
9000,9000 17736.9
10000,10000 22329.8
但是,在上面的动态规划算法中,有一个致命的地方就是数组分配,
int[,] calculateTable, 如果m,n较大,马上就内存溢出。
假定N = 1, 则可能的整数集这能为{1}, 所以个数为1。
假定M = 1,N > 1, 则不可能有合适的整数集,所以个数为0。
假定M > N, 则结果集的个数和M = N的一样多, 因为不可能出现比N大的数。
假定M <= N, M > 1, N > 1, 此时我们有两种情况,结果集中包括M, 或者不包括。最终的数量为这两种情况的数量之和。
假定我们用F(N,M)来表示结果集的数量。则有如下表达式:
F(1,M) = 1; M为任意值.
F(N,1) = 0; N > 1.
F(N,M) = F(N,N); M > N.
F(N,M) = F(N - M,M - 1) + F(N,M - 1);M <= N, M > 1, N > 1.
一般有两种方式来解决这种问题,递归和动态规划。
递归方式:
public static int GetPartitionCount2(int number, int maxElement)
{
if (number == 1)
return 1;
if (maxElement == 1)
return 0;
if (maxElement < number)
return GetPartitionCount2(number - maxElement, maxElement - 1) + GetPartitionCount2(number, maxElement - 1);
else
return 1 + GetPartitionCount2(number, number - 1);
}
动态规划:
public static int GetPartitionCount1(int number, int maxElement)
{
int[,] calculateTable = new int[number + 1, maxElement + 1];
calculateTable[1, 1] = 1;
for (int i = 2; i < number + 1; i++)
{
calculateTable[i, 1] = 0;
}
for (int i = 1; i < maxElement + 1; i++)
{
calculateTable[1, i] = 1;
}
for (int i = 2; i < maxElement + 1; i++)
{
for (int j = 2; j < number + 1; j++)
{
if(i<j)
calculateTable[j, i] = calculateTable[j - i, i - 1] + calculateTable[j, i - 1];
else if(i>=j)
calculateTable[j, i] = 1 + calculateTable[j, j - 1];
}
}
return calculateTable[number, maxElement];
}
就递归方式而言,最大的不好之处就是递归次数太多,做了太多的冗余计算。
(N,M) 所需时间(ms)
100,100 250
120,120 1281.3
140,140 6297
160,160 27484.7
180,180 110844.5
200,200 398417.4
相对而言,动态规划效率要高很多,不是一个数量级的。
(N,M) 所需时间(ms)
1000,1000 46
2000,2000 187.5
3000,3000 593.8
4000,4000 1719.0
5000,5000 3172.3
6000,6000 5657.0
7000,7000 9345.1
8000,8000 13220.6
9000,9000 17736.9
10000,10000 22329.8
但是,在上面的动态规划算法中,有一个致命的地方就是数组分配,
int[,] calculateTable, 如果m,n较大,马上就内存溢出。