3-1最优调度
3-2任务批处理
*3-3石子合并问题
石子合并问题中首先令dp[i][j](i<j)代表石子堆i到石子堆j的情况,分为最大得分情况和最小得分情况,每次的得分等于不同情况下的得分加从i到j的和例如在数字为4,4,5,9时:
dp[1][2]=dp[1][1]+dp[2][2]=]=0+0+sum12=4+4=8;
dp[2][3]=dp[2][2]+dp[3][3]+sum23=9;
dp[1][3]=dp[1][1]+dp[2][3]=0+sum13+dp[2][3]=13+9=22
=dp[1][2]+dp[3][3]=8+sum13=8+13=21;………
所以由递推公式可知需要先计算dp[1][2],dp[2][3].dp[3][4]……然后通过数组进一步求解其他的情况,最终得到dp[1][4],因为每次都有不同情况所以需要对每种情况进行比较,将最小得分情况存入一个数组,最大得分情况存入另一个数组。最终得到两个数组中i=1,j=4的对应位置的值
最小得分数组:
需要三重循环,对二维数组填数从红框标注的开始依次向上填最终得到最小分数。
最大得分数组同理
代码:
1. #include<iostream>
2. #include<algorithm>
3. #include<fstream>
4. using namespace std;
5. int dpmin[1024][1024] = { 0 }, dpmax[1024][1024] = { 0 };//一个存放最大一个存放最小
6. int sum[10] , a[10];
7. void Intereslove(int n) {
8. for (int length = 2; length <= n; ++length) {
9. for (int begin = 1; begin <= n - length + 1; ++begin) {
10. int end = begin + length - 1;
11. dpmin[begin][end] = 200000;//由题目可得最多为200000个石子如dp[i][j]表示1-j的最大合并得分
12. dpmax[begin][end] = -1;//设置一个负数
13. int temp = sum[end] - sum[begin - 1];//从begin开始到end结束的数字和
14. for (int k = begin; k < end; ++k) {
15. dpmin[begin][end] = min(dpmin[begin][end], dpmin[begin][k] + dpmin[k + 1][end] + temp);
16. dpmax[begin][end] = max(dpmax[begin][end], dpmax[begin][k] + dpmax[k + 1][end] + temp);
17. }
18. }
19. }
20. }
21. void main() {
22. ifstream inFile("input3-3.txt");
23. ofstream outFile("output3-3.txt");
24. int n, datalen=0;
25. int nm[10];
26. while (!inFile.eof()) {
27. inFile >> nm[datalen++];
28. }
29. int a[10];
30. n = nm[0];
31. for (int i = 0; i < n; i++)a[i] = nm[i+1] ;
32. sum[0] = 0;
33. for (int i = 1; i <= n; i++) {
34. sum[i] = sum[i - 1] + a[i-1];
35. }
36. Intereslove(n);
37. outFile << dpmin[1][4]<<endl;
38. outFile << dpmax[1][4];
39. inFile.close();
40. outFile.close();
41.
42. }
结果:
3-4数字三角形问题
每次选择一个位置往上走,然后找最优值,每次可以选很多种走法,每次找最优,i,j表示位置
数字三角形问题,从顶部向下,每次都可以走(I,j),(i,j+1)两种走法所以需要的数组中存储的是从(i,j)走的最大的路径,因此用D[i][j]存储点(i,j),maxsum[i][j]存储点(i,j)到底边路径的最大和。由于点(i,j)只能向(i+1,j)和(i+1,j+1)走,可以自底向上计算从最后一行往上计算,算完的值存在maxsum中,最后一行不需要行走所以可以全部赋值为-1,将第四行全部置为数组元素值,然后依次向顶部计算。
Max[i][j]=max(Max[i+1][j],Max[i+1][j+1])+a[i][j]
1. #include <iostream>
2. #include <algorithm>
3. #include<fstream>
4. using namespace std;
5. int D[20][20];
6. int n;
7. int maxsum[20][20] = { 0 };
8. //递推法,从下往上计算
9. void Shorest(){
10. for (int j = 0; j < n; j++)
11. maxsum[n - 1][j] = D[n - 1][j];
12. for (int i = n - 2; i >= 0; i--)
13. for (int j = 0; j <= i; j++)
14. maxsum[i][j] = max(maxsum[i + 1][j], maxsum[i + 1][j + 1]) + D[i][j];
15. cout << maxsum[0][0];
16. }
17. void main()
18. {
19. ifstream inFile("input3-4.txt");
20. ofstream outFile("output3-4.txt");
21. inFile >> n;
22. for (int i = 0; i <= n; i++) {
23. for (int j = 0; j <= i; j++) {
24. inFile>> D[i][j];
25. }
26. }
27. Shorest();
28. }
结果:
3-5乘法表
3-6租用游艇
3-7汽车加油行驶
3-8最小m段和问题
dp(i,j)表示i个数分为j段,所以有
dp[i][j]=min(max{dp[k][j],sum(k,i)},max{dp[j][j-1]),a[i]}) k =j:i-1
分析:
在这个式子中的每种情况首先计算对于前k个数划分为j段的情况与从k加到最后的j位数的最大值,然后与从前k个数划分j-1段,即把最后一个数字单独作为一段的情况,计算两个值的最小值
对dp数组进行初始化,初始化dp[1][1],dp[2][1],dp[3][1]……就是把所有的数都只分为一个段,也就是前i个数的和。进行累计计算
用5个数字举例
假设数字是2,1,3,0,4
I表示数的个数,j表示段数
数组:
通过查表即可以得到最终答案。
最开始红色部分为初始化过的,然后用递推公式依次计算得到数据,例如:
dp[5][1]=min{max{dp[4][2],sum45},max{dp[3][2],sum35},max{dp[4][1],s[5]}
在这里插入图片描述
代码:
1. #include<iostream>
2. #include<fstream>
3. #include<algorithm>
4. using namespace std;
5. int dpm[6][6] = { 0 };
6. int n = 5;
7. void min_m(int *a) {
8. for (int i = 1; i <= n; i++)
9. for (int j = 2; j < i; j++) {
10. dpm[i][j]= max(dpm[i - 1][j - 1], a[i - 1]);//dpm数组从1开始,a从零开始
11. for (int k = j; k <i; k++) {
12. if (dpm[i][j] > max(dpm[k][j], dpm[i][1] - dpm[k - 1][1]))
13. dpm[i][j] = max(dpm[k][j], dpm[i][1] - dpm[k - 1][1]);
14. }
15. }
16. }
17. int sum(int* a, int i) {
18. int res=0;
19. for (int j = 0; j < i; j++) {
20. res += a[j];
21. }
22. return res;
23. }
24. void main() {
25. int a[5] = { 2,1,3,0,4 };
26. //dpm数组进行初始化
27. for (int i = 1; i <= n; i++) {
28. dpm[i][1] = sum(a, i);
29. dpm[i][i] = *max_element(a,a+i);
30. }
31. min_m(a);
32. }
3-10最大长方体问题