使用一個工作數組來記錄每個位置的相關狀態
1、最小分配數
2、最大分配數
3、剩餘分配數
其實也就是遞迴的一個思想,分配掉第一個以後剩餘的還是一個分解問題,只不過是總分配數,項數都變化了
例如: sum = 10, n = 4, min = 1, max = 5 的工作數組以及初始拆分數組如下
| 拆分結果數組 |
|
1 |
1 |
3 |
5 |
| |
|
|
|
|
|
| 當前位置最小值 |
|
1 |
1 |
3 |
5 |
| 當前位置最大值 |
|
2 |
2 |
4 |
5 |
| 剩餘分配數 |
|
9 |
8 |
5 |
0 |
另一個核心是在迴圈嵌套中使用一個標誌量來指向最靠右的未達到該位置最大分配數的下標
使用這個標誌量讓迴圈反覆多次執行,實現迭代的效果
public string PrintDivision(int sum, int n, int min, int max)
{
//定義返回變數
string result;
StringBuilder builder = new StringBuilder();
//判斷輸入有效性
if (min * n > sum || max * n < sum)
result = "無解\n";
else
{
//定義拆分數組
//從左至右代表輸出運算式的每個拆分結果,也就是當前取值
int[] arr = new int[n];
//定義工作數組
//第1行代表每個位元上的最小取值
//第2行代表每個位元上的最大取值
//第3行代表累計到當前位置的累計總和
int[,] arrWork = new int[3, n];
//定義臨時計算的最大值
int maxValue;
//局部待分配值
int subSum = sum;
//初始化工作數組
for (int i = 0; i < n; i++)
{
//第i個位置以後的位置都取最大值的結果
maxValue = max * (n - i - 1);
//第i個位置上的允許最小值
arrWork[0, i] = maxValue >= subSum ? min : subSum - maxValue;
//第i個位置上的允許最大值
arrWork[1, i] = subSum / (n - i);
//除了最後一個,其他位置都以最小值初始化,最後一個以最大值初始化
arr[i] = i < n - 1 ? arrWork[0, i] : arrWork[1, i];
//累加現有分配結果
subSum -= arr[i];
//累加結果儲存到工作數組
arrWork[2, i] = subSum;
}
//添加初始分配到輸出
builder.Append(string.Join("+",Array.ConvertAll<int,string>(arr,Convert.ToString)));
builder.Append("\n");
//精華哦,這個變數標誌需要進行重新排列的起點
int startpoint = n - 2;
//產生全部剩餘拆分結果
//從倒數第二個位置開始重設分配數,一直到左邊第一個位置結束
//另外由於標誌變數的關係,這個for迴圈的執行次數會比這裡表面看到的多很多
for (int i = n-2; i >= 0; i--)
{
//從第i個位置的對應最小值加1開始迴圈到本位置的最大值
//這裡直接更改本位置分配數以及累計數,比較搞腦子哦。
//標準情況下不推薦這麼寫程式,不是良好的編碼風格
//這裡是因為少搞幾個變數出來 for (arr[i]++, arrWork[2, i]--; arr[i] <= arrWork[1, i]; arr[i]++, arrWork[2, i]--)
{
//本位置的分配數已經變化,下面重新分配後續位置
subSum = arrWork[2, i];
min = arr[i];
for (int j = i + 1; j < n; j++)
{
//第j個位置以後的位置都取最大值的結果
maxValue = max * (n - j - 1);
//第j個位置上的允許最小值
//按待處理數計算理論最小值
arrWork[0, j] = maxValue >= subSum && subSum-maxValue<min ? min : subSum - maxValue;
//如果理論最小值小於左邊的數字,則使用左邊的數字
arrWork[0, j] = arrWork[0, j]>min?arrWork[0, j]:min;
//第j個位置上的允許最大值
arrWork[1, j] = subSum / (n - j);
//除了最後一個,其他位置都以最小值初始化,最後一個以最大值初始化
arr[j] = j < n - 1 ? arrWork[0, j] : arrWork[1, j];
//累加現有分配結果
subSum -= arr[j];
//累加結果儲存到工作數組
arrWork[2, j] = subSum;
//當一個位置沒有達到該位置的最大值的時候,下次的重設起點就要從這裡開始。這裡是形成類似遞迴的關鍵位置
if (arr[j] < arrWork[1, j])
startpoint = j;
}
//重設起點
i = startpoint;
//完成一次分解,輸出
builder.Append(string.Join("+", Array.ConvertAll<int, string>(arr, Convert.ToString)));
builder.Append("\n");
}
}
result =builder.ToString();
}
return result;
}
謝謝大家來觀賞哦
ps:源碼
源碼已經更新,添加了Dragonpig的演算法,並修正我的原有代碼
添加了次數統計和時間檢測
按照Dragonpig的思路,在擷取剩餘排列時的代碼可以簡化如下
for (int j = i + 1; j < n; j++)
{
//第j個位置上的允許最大值
arrWork[1, j] = subSum / (n - j);
//計算並判斷當前位置取值
arr[j]=Math.Max(min,subSum-max * (n - j - 1 )); //主要就是這個不同啦
//累加現有分配結果
subSum -= arr[j];
//累加結果儲存到工作數組
arrWork[2, j] = subSum;
//當一個位置沒有達到該位置的最大值的時候,下次的重設起點就要從這裡開始
if (arr[j] < arrWork[1, j])
startpoint = j;
}
的確簡潔不少了