软件设计师考试经典基础算法(分治法、回溯法、动态规划法、贪心法、堆排序、归并排序)含有详细注释
所有代码和注释都是本人逐个敲出来,参考了软件设计师考试指导教材里的C代码,现全部改用C#编写。如有问题,欢迎留言探讨。
https://gitee.com/elite216/algorithms/blob/master/Form1.cs
1 public partial class Form1 : Form 2 { 3 static int N = 12; 4 int[] b = new int[N + 1]; //用于标记1-N这些数字是否已经被使用(方格填数问题) 5 int[] a = new int[10]; //方格填数问题 6 //checkMatrix矩阵用于方格填数问题,{5,7,-1}表示所填的数要与5、7两个位置的数比较 7 //{-1,-1,-1}表示不需要与任何数比较 8 int[,] checkMatrix = { { -1, -1, -1 }, { 0, -1, -1 }, { 1, -1, -1 }, 9 { 0, -1, -1 }, {1,3,-1},{2,4,-1}, 10 {3,-1,-1},{4,6,-1},{5,7,-1}}; 11 12 int[] deltai = { 2, 1, -1, -2, -2, -1, 1, 2 }; //用于马的遍历问题 13 int[] deltaj = { 1,2,2,1,-1,-2,-2,-1 }; //用于马的遍历问题 14 int[,] board = new int[8, 8]; //用于马的遍历问题,标记棋盘各坐标是否已用 15 16 17 public Form1() 18 { 19 InitializeComponent(); 20 } 21 22 private int [] GetNext(string pstr) //获取模式串的next数组 23 { 24 int len=pstr.Length; //模式串的长度 25 26 string[] arr=new string[len]; //模式串各字符数组 27 28 int[] next = new int[len+1]; //next函数数组,多取一位避免while循环时溢出 29 30 for (int k = 0; k < len; k++) 31 { 32 arr[k] = pstr.Substring(k,1); //模式串字符数组赋值 33 } 34 35 next[0] = -1; //赋初始值 36 int j = -1; 37 int i = 0; 38 39 while (i < len) 40 { 41 if (j == -1 || arr[i] == arr[j]) { ++i; ++j; next[i] = j; } //字符串第i字符和第i-1字符相同,i、j都加1 42 else j = next[j]; //否则重新设置j的开始值 43 } 44 45 for (int k = 0; k < arr.Length; k++) //显示模式串及其next函数值 46 { 47 if (k == 0) { label1.Text = "原字符串:"; label2.Text = "next函数值:"; } 48 label1.Text += arr[k]; 49 label2.Text += next[k].ToString()+"、"; 50 } 51 return next; 52 } 53 54 private void button2_Click(object sender, EventArgs e) //主串与模式串比较 55 { 56 string mstr = textBox2.Text; //主串 57 int mlen = mstr.Length; //主串长度 58 string[] marr = new string[mlen]; //主串字符数组 59 for (int k = 0; k < mlen; k++) //主串数组赋值 60 { 61 marr[k] = mstr.Substring(k,1); //把字符串中的字符逐个分解至数组中 62 } 63 64 int[] next = GetNext(textBox1.Text); //获得next函数值 65 66 string pstr=textBox1.Text; //获得输入的模式串 67 int plen = pstr.Length; //模式串的长度 68 string [] parr=new string[plen]; //模式串字符数组 69 70 for (int k = 0; k < plen; k++) //模式串数组赋值 71 { 72 parr[k] = pstr.Substring(k,1); 73 } 74 75 int i = -1; 76 int j = -1; 77 while (i < mlen && j < plen) 78 { 79 if (j == -1 || marr[i] == parr[j]) { ++i; ++j; } 80 else { j = next[j]; } 81 } 82 if (j >= plen) label5.Text = "模式串存在,位于" + (i - plen).ToString(); //i-plen是模式串位于主串的位置(起始位置) 83 else label5.Text = "模式串不存在!"; 84 } 85 86 private void button3_Click(object sender, EventArgs e)//迭代二分查找法求方程的根 87 { 88 double i = this.mycuba(0.00001, 1.0, 2.0); 89 MessageBox.Show(i.ToString(), "j=", MessageBoxButtons.OK); 90 } 91 private double mycuba(double f, double a, double b) //二分查找法解方程 92 { 93 double i = a + (b - a) / 2; 94 while (System.Math.Abs(i * i * i-5) > f) 95 { 96 if (i * i * i - 5 > 0) 97 { 98 b =a+ (b - a) / 2; 99 } 100 else 101 { 102 a = a+ (b - a) / 2; 103 } 104 i = a + (b - a) / 2; 105 // MessageBox.Show(i.ToString(), "i=", MessageBoxButtons.OK); 106 } 107 return i; 108 } 109 110 private void merge(int [] A,int p, int q, int r) //分治法归并排序的归并 111 { 112 //pa(a,"a"); 113 MessageBox.Show("P="+p.ToString()+",Q="+q.ToString()+",R="+r.ToString(),"合并", MessageBoxButtons.OK); 114 pa(A, "A数列的值"); 115 int n1 = q - p + 1; 116 int n2 = r - q; 117 int i, j, k; 118 int[] L = new int[50]; 119 int[] R = new int[50]; 120 for (i = 0; i < n1; i++) 121 { 122 L[i] = A[p+i]; 123 } 124 125 for (i = 0; i < n2; i++) 126 { 127 //MessageBox.Show(i.ToString(), "右侧i值", MessageBoxButtons.OK); 128 R[i] = A[q +1+ i]; 129 } 130 //MessageBox.Show(n1.ToString(), "n1值", MessageBoxButtons.OK); 131 L[n1] = 100; 132 133 R[n2] = 100; 134 // pa(L, "左侧"); 135 136 i = 0; 137 j = 0; 138 //pa(R, "右侧"); 139 140 for (k = p; k < r + 1; k++) 141 { 142 if (L[i] < R[j]) 143 { 144 A[k] = L[i]; 145 i++; 146 } 147 else 148 { 149 A[k] = R[j]; 150 j++; 151 } 152 } 153 //pa(a, "归并后的a"); 154 } 155 private void mergesort(int[] A, int p, int r) //分治法归并排序 156 { 157 int q; 158 159 if (p < r) 160 { 161 q = (p + r) / 2; 162 // MessageBox.Show("p="+p.ToString(), "排序中的q="+q.ToString(), MessageBoxButtons.OK); 163 mergesort(A, p, q);//排序前半 164 mergesort(A, q+1, r); //排序后半 165 merge(A, p, q, r); //两半归并 166 } 167 168 } 169 170 private void button4_Click(object sender, EventArgs e) //分治排序主程序 171 { 172 int [] a={ 7,4,9,8,10,2,3,11,55,23}; 173 mergesort(a, 0, 9); 174 pa(a,"final"); 175 } 176 177 private void pa(int[] a, string b) //通用方法,用于显示数组 178 { 179 string s = ""; 180 for (int i = 0; i < a.Length; i++) 181 { 182 s = s + a[i].ToString()+"、"; 183 } 184 s = s.Substring(0, s.Length - 1); 185 MessageBox.Show(b, "q=" + s.ToString(), MessageBoxButtons.OK); 186 } 187 188 private void button5_Click(object sender, EventArgs e) //0-1背包问题的动态规划法,主程序 189 { 190 int n=5, W=17; //n是物品数量,W是背包能装的总重量 191 int[] weights = {2,2,6,5,4}; //物品的重量 192 int[] values = {6,3,5,4,6}; //物品的价值 193 int []x=new int [n]; //x数组,用0表示不装某物品,1表示装入某物品 194 int[,] c = new int[n, W]; //二维数组,相当于创建表格,后续填表 195 c = pack(n, W, weights, values); 196 int i; 197 for (i = n; i > 1; i--) 198 { 199 if (c[i, W] == c[i - 1, W]) 200 { 201 x[i - 1] = 0; 202 } 203 else 204 { 205 x[i - 1] = 1; 206 W = W - weights[i - 1]; 207 } 208 } 209 if (c[1, W] == 0) 210 { 211 x[0] = 0; 212 } 213 else 214 { 215 x[0] = 1; 216 } 217 pa(x, "背包"); //显示结果 218 219 } 220 private int[,] pack(int n, int W, int[] weights, int[] value) //填表 221 { 222 int i, w; 223 int [ ,] c=new int [n+1,W+1]; 224 for (w = 0; w <= W; w++) 225 { 226 c[0, w] = 0; //首列都为0 227 } 228 for (i = 1; i <= n; i++) 229 { 230 c[i, 0] = 0; //首行都为0 231 for (w = 1; w <= W; w++) //逐行填写 232 { 233 if (weights[i - 1] <= w) //如果背包剩余重量大于物品重量 234 { 235 if (value[i - 1] + c[i - 1, w - weights[i - 1]] > c[i - 1, w]) 236 { 237 c[i, w] = value[i - 1] + c[i - 1, w - weights[i - 1]]; //放入第i-1个物品 238 } 239 else 240 { 241 c[i, w] = c[i - 1, w]; //不放入第i-1个物品 242 } 243 } 244 else 245 { 246 c[i, w] = c[i - 1, w]; //不放入第i-1个物品 247 } 248 } 249 } 250 return c; 251 } 252 253 private void button6_Click(object sender, EventArgs e) //动态规划法最长公共子串问题 254 { 255 string[] x = { "A", "B", "C", "B", "D", "A","B"}; 256 string[] y = { "B", "D", "C", "A", "B", "A"}; 257 int j = x.Length; 258 int i = y.Length; 259 string [,] L=new string[i+1,j+1]; 260 int[,] B = new int[i + 1, j + 1]; 261 string[,] C = new string[i + 1, j + 1]; 262 B[0, 0] = 0; 263 C[0, 0] = "※0"; 264 L[0, 0] = "※"; 265 for (int ii = 1; ii <= i; ii++) 266 { 267 B[ii, 0] = 0; 268 L[ii, 0] = "※"; 269 C[ii, 0] = "※0"+y[ii-1]; 270 for (int jj = 1; jj <= j; jj++) 271 { 272 B[0, jj] = 0; 273 C[0, jj] = "※"+x[jj-1]; 274 L[0, jj] = "※"; 275 if (y[ii-1] == x[jj-1]) 276 { 277 B[ii, jj] = B[ii - 1, jj - 1] + 1; 278 L[ii, jj] = "♂"; 279 C[ii, jj] = "♂" + B[ii, jj].ToString(); 280 } 281 else 282 { 283 if (B[ii - 1, jj] >= B[ii, jj - 1]) 284 { 285 B[ii, jj] = B[ii - 1, jj]; 286 L[ii, jj] = "↑"; 287 C[ii, jj] = "↑" + B[ii, jj].ToString(); 288 } 289 else 290 { 291 B[ii, jj] = B[ii, jj-1]; 292 L[ii, jj] = "←"; 293 C[ii, jj] = "←" + B[ii, jj].ToString(); 294 } 295 } 296 } 297 } 298 299 int len=B[i, j]; 300 for (int f = 0; f <= j; f++) 301 { 302 303 } 304 305 toDatagrid(C); 306 307 } 308 309 private void toDatagrid(string [,] C) //通用函数,输出字符数组到datagridview 310 { 311 DataTable dt = new DataTable(); 312 313 for (int kk = 0; kk < C.GetLength(1); kk++) 314 dt.Columns.Add(kk.ToString(), typeof(string)); 315 316 for (int p = 0; p < C.GetLength(0); p++) 317 { 318 DataRow dr = dt.NewRow(); 319 for (int k = 0; k < C.GetLength(1); k++) 320 dr[k] = C[p, k]; 321 dt.Rows.Add(dr); 322 } 323 dataGridView1.DataSource = dt; 324 } 325 326 private void toDatagrid2(int[,] C) //通用函数,输出二维整数数组到datagridview 327 { 328 DataTable dt = new DataTable(); 329 330 for (int k = 0; k < C.GetLength(1); k++) 331 dt.Columns.Add(k.ToString(), typeof(string)); 332 333 for (int p = 0; p < C.GetLength(0); p++) 334 { 335 DataRow dr = dt.NewRow(); 336 for (int k = 0; k < C.GetLength(1); k++) 337 dr[k] = C[p,k].ToString(); 338 dt.Rows.Add(dr); 339 } 340 dataGridView1.DataSource = dt; 341 } 342 343 private void toDatagrid3(double[,] C) //通用函数,输出双精度数数组到datagridview 344 { 345 DataTable dt = new DataTable(); 346 347 for (int k = 0; k < C.GetLength(1); k++) 348 dt.Columns.Add(k.ToString(), typeof(string)); 349 350 for (int p = 0; p < C.GetLength(0); p++) 351 { 352 DataRow dr = dt.NewRow(); 353 for (int k = 0; k < C.GetLength(1); k++) 354 dr[k] = C[p, k].ToString(); 355 dt.Rows.Add(dr); 356 } 357 dataGridView1.DataSource = dt; 358 } 359 360 private void toGrid(int [] a) //通用函数,输出一维整数数组到datagridview 361 { 362 DataTable dt = new DataTable(); 363 for (int kk = 0; kk < a.Length; kk++) 364 dt.Columns.Add(kk.ToString(), typeof(string)); 365 for (int i = 0; i < a.Length; i++) 366 { 367 DataRow dr = dt.NewRow(); 368 dr[i] = a[i]; 369 dt.Rows.Add(dr); 370 } 371 dataGridView1.DataSource = dt; 372 } 373 374 private void button7_Click(object sender, EventArgs e) //回溯法背包问题主方法 375 { 376 float[] values = {11,21,31,33,43,53,55,65}; //物品的价值数值:已按单位价值排序 377 int[] weights = {1,11,21,23,33,43,45,55}; //物品的重量:已按单位价值排序 378 float[] VW=new float [values.Length]; //物品单位重量价值:价值/重量,已从大到小排序 379 int W = 110; //背包总容量 380 for (int i = 0; i < values.Length; i++) 381 { 382 VW[i] = values[i] / weights[i]; //物品单位重量价值:价值/重量,已从大到小排序 383 } 384 385 int current_weight = 0; //当前背包重量 386 float current_profit = 0; //当前背包价值 387 int weight = 0; //背包初始重量 388 float profit = -1; //背包初始价值 389 int index = 0; 390 int[] X = new int[values.Length]; 391 int[] Y = new int[values.Length]; 392 393 while (true) 394 { 395 while (index < values.Length && current_weight + weights[index] <= W) //深度优先,装到不能再装为止 396 { 397 current_profit += values[index]; 398 current_weight += weights[index]; 399 Y[index] = 1; 400 index++; 401 } 402 if (index >= values.Length-1) //如果已经到达叶子结点 403 { 404 weight = current_weight; 405 profit = current_profit; 406 index = values.Length-1; 407 for (int k = 0; k < values.Length; k++) //Y数组就是一个可行解 408 { 409 X[k] = Y[k]; 410 } 411 } 412 else 413 { 414 Y[index] = 0; //背包已装满,后续结点不再装 415 } 416 417 while (Bound(values, weights, VW, W, current_profit, current_weight, index) <= profit) //可获得最大值小于现值时,回溯 418 { 419 while (index != 0 && Y[index] != 1) //回溯到上一个选中的节点 420 { 421 index--; 422 } 423 if (index == 0) //如果已回溯到根节点,结束所有循环 424 { 425 toGrid(X); //将数组X作为结果输出 426 return; 427 } 428 Y[index] = 0; //将该节点设为不选 429 current_profit -= values[index]; //减去相应价值 430 current_weight -= weights[index]; //减去相应重量 431 } 432 index++; 433 } 434 435 } 436 437 private float Bound(float[] values, int[] weights, float[] VW, int W, float profit_gained, int weight_used, int k) //当前背包余量下,可获得的最大价值 438 { 439 for (int i = k + 1; i < weights.Length; i++) //搜索可行解 440 { 441 if (weight_used + weights[i] <= W) 442 { 443 profit_gained += values[i]; 444 weight_used += weights[i]; 445 } 446 else 447 { 448 profit_gained += VW[i] * (W - weight_used); 449 weight_used = W; 450 return profit_gained; 451 } 452 } 453 return profit_gained; 454 } 455 456 private void button8_Click(object sender, EventArgs e) //回溯法n皇后问题 457 { 458 int n=8; //4X4方格 459 int [] Column_Num=new int [n+1]; //一维数组的序号代表行,值代表列 460 int index = 1; //代表行 461 int answer_num=0; 462 for (int i = 1; i <= n; i++) 463 { 464 Column_Num[i] = 0; //赋初值 465 } 466 toGrid(Column_Num); 467 while (index > 0) 468 { 469 Column_Num[index]++; //从第1列开始,如果回溯则在上一次的列的基础上尝试下一列 470 while (Column_Num[index] <= n && Place(Column_Num, index,n)==0) //为0表示不可行 471 { 472 Column_Num[index]++; //如果不可行移至下一列再试,Column_Num[index]的值可能超过n 473 } 474 if (Column_Num[index] <= n) //已放至最后一列 475 { 476 if (index == n) //放置最后一个皇后成功 477 { 478 answer_num++; 479 MessageBox.Show("完成", "完成"); 480 toGrid(Column_Num); 481 return; 482 } 483 else 484 { 485 index++; //查找下一行 486 Column_Num[index] = 0; 487 } 488 } 489 else 490 { 491 index--; //无法放置,回溯上一行 492 493 } 494 } 495 496 } 497 498 private int Place(int [] Column,int index,int n) //检查所放的皇后位置可不可行 499 { 500 int i; //代表行 501 for(i=1;i<index;i++) //遍历第index行之前的所有行 502 { 503 int Column_differ=System.Math.Abs(Column[index]-Column[i]); //列的差 504 int Row_differ=System.Math.Abs(index-i); //行的差 505 if(Column[i]==Column[index] || Column_differ==Row_differ || Column[i]>n) //行的差/列的差等于1时,表示斜线上不可行 506 { 507 return 0; //不可行 508 } 509 } 510 return 1; //可行 511 } 512 513 private void button9_Click(object sender, EventArgs e) //矩阵链乘问题,主方法 514 { 515 int[] p = { 3, 4, 30, 5, 16, 20, 25 }; //矩阵的p数组,代表A1矩阵为3X4、A2矩阵为4X30…… 516 int[,] m = new int[8,8]; 517 int[,] s = new int[8,8]; 518 for (int i = 0; i < 8; i++) 519 { 520 m[i,0] = i; 521 s[i, 0] = i; 522 for (int j = 1; j < 8; j++) 523 { 524 m[i,j] = 0; 525 s[i,j] = 0; 526 } 527 } 528 MatrixChain(p, m, s); 529 toDatagrid2(s); 530 result(s,1,6); 531 } 532 void MatrixChain(int[] p, int[,] m, int[,] s) //矩阵链乘问题 533 { 534 int N=6; //总共N个矩阵,矩阵个数比p数组的长度少1 535 int i, j, k, t; 536 for ( i = 0; i <= N; i++) 537 { 538 m[i,i] = 0; 539 } 540 541 for ( t = 2; t <= N; t++) //链乘矩阵的个数,至少为2,最多为N个 542 { 543 for ( i = 1; i <= N - t+1; i++) //t个矩阵相乘,从下标为1的矩阵开始,找t个相连的矩阵 544 { 545 j = i + t -1; // 共t个矩阵相乘,超始下标是i,终止下标就是i+t-1 546 m[i,j] = 1000000; // 设置一个最大代价 547 for (k = i; k < j; k++) 548 { 549 int temp = m[i,k] + m[k + 1,j] + p[i - 1] * p[k] * p[j]; 550 if (temp < m[i,j]) 551 { 552 m[i,j] = temp; 553 s[i,j] = k; 554 } 555 } 556 } 557 } 558 } 559 560 void result(int[,] s, int i, int j) //矩阵链乘问题结果显示 561 { 562 563 if (i == j) 564 { 565 label7.Text = label7.Text + "A" + i.ToString(); 566 } 567 else 568 { 569 label7.Text = label7.Text + "("; 570 result(s, i, s[i, j]); 571 result(s, s[i, j] + 1, j); 572 label7.Text = label7.Text + ")"; 573 } 574 575 } 576 577 private void button10_Click(object sender, EventArgs e) //堆排序,主方法 578 { 579 int[] data = {55,60,40,10,80,65,15,5,75}; 580 int n = data.Length; 581 for (int i =0 ; i <= n / 2 - 1; i++) 582 { 583 HeapAdjust(data, i, n - 1); 584 } 585 HeapAdjust(data, 0, n - 1); 586 pa(data, "大根"); //显示 587 } 588 private void HeapAdjust(int [] data, int s, int m) //堆排序,组建大根堆 589 { 590 int tmp, j; 591 tmp = data[s]; 592 for (j = 2 * s + 1; j <= m; j = j * 2 + 1) 593 { 594 if (j < m && data[j] < data[j + 1]) 595 { 596 j++; 597 } 598 if (tmp > data[j]) 599 { 600 break; 601 } 602 data[s] = data[j]; 603 s = j; 604 } 605 data[s] = tmp; 606 } 607 608 private void button11_Click(object sender, EventArgs e) //n凸多边形的切割问题 609 { 610 int n = 6; //多边形的边数 611 double[,] t=new double [n,n]; //t[i,j]表示从i-1点开始至j点构成的多边形的最优权值 612 int[,] s = new int[n, n]; //s[i,j]=k,表示由点i-1,j,k这三个点构成一个三角形 613 for (int i = 1; i < n; i++) 614 { 615 t[i, i] = 0; 616 } 617 for (int r = 2; r <= n; r++) //问题规模,如t[i,j]时,j和i相隔r条边;最大可选r=n,相当于终点与起点重合 618 { 619 for (int i = 1; i < n - r+1; i++) 620 { 621 int j = i + r - 1; //距离i点最远的点的编号,当r=n时最大取值为n-1 622 623 t[i, j] = t[i + 1, j] + weight(i - 1, i, j); 624 s[i, j] = i; 625 for (int k = i + 1; k < j; k++) 626 { 627 double temp = t[i, k] + t[k + 1, j] + weight(i - 1, k, j); 628 if (temp < t[i, j]) 629 { 630 t[i, j] = temp; 631 s[i, j] = k; 632 } 633 } 634 } 635 } 636 toDatagrid2(s); //输出最佳方案切割位置点 637 //toDatagrid3(t); //输出最终权值 638 } 639 private double weight(int i, int j, int k) //求权值 640 { 641 double[,] w = {{0,5,8.1,9.2,9.8,10}, 642 {5,0,3.2,4.5,5.7,6.7}, 643 {8.1,3.2,0,1.4,3.2,5}, 644 {9.2,4.5,1.4,0,2,4.1}, 645 {9.8,5.7,3.2,2,0,2.2}, 646 {10,6.7,5,4.1,2.2,0}}; 647 if (k == 6) k = 0; //数组从0到5编号,因此多边形的第6个点与第0点重合 648 return w[i, j] + w[j, k] + w[i, k]; 649 } 650 651 private void button12_Click(object sender, EventArgs e) //递归回溯法:在方阵里填数,使相邻数相加为质数 652 { 653 //初始化数据 654 655 int[,] a = new int[3, 3]; //数组,用于存放方阵里的数 656 bool[] vis = new bool[N+1]; //标志用于记录1到10这十个数是否已经被使用 657 for (int i = 0; i <= N; i++) 658 { 659 vis[i] = false; 660 } 661 662 List<int> list = new List<int>(); //list用于记录所有结果 663 dfs(a, 0, 0, vis, list); 664 665 //输出得到的结果list 666 string s=""; 667 for (int i = 0; i < list.Count; i++) 668 { 669 s += list[i].ToString()+"、"; 670 if ((i + 1) % 9 == 0) { s = s.Substring(0, s.Length - 1) + "\n"; } 671 } 672 MessageBox.Show((list.Count/9).ToString(), "总共数量", MessageBoxButtons.OK, MessageBoxIcon.Information); 673 MessageBox.Show(s, "", MessageBoxButtons.OK, MessageBoxIcon.Information); 674 } 675 private bool isprime(int n) //判断一个整数是不是质数 676 { 677 for (int i = 2; i * i <= n; i++) 678 { 679 if (n % i == 0) return false; 680 } 681 return true; 682 } 683 private bool check(int [,]a,int x, int y, int k) //检查k跟它的上、左位置的数相加是否为质数 684 { 685 if (y>0 && !isprime(a[x, y - 1] + k)) return false; //与左侧数相加不是质数 686 if (x>0 && !isprime(a[x - 1, y] + k)) return false; //与上方数相加不是质数 687 return true; 688 } 689 private void dfs(int[,] a, int x, int y, bool[] vis, List<int> list) 690 { 691 if (x == 3) //x=3说明遍历完成,开始输出数组 692 { 693 for (int i = 0; i < 3; i++) 694 { 695 for (int j = 0; j < 3; j++) 696 { 697 list.Add(a[i,j]); 698 } 699 } 700 return; 701 } 702 for (int i = 1; i <= N; i++) 703 { 704 if (!vis[i] && check(a, x, y, i)) 705 { 706 a[x, y] = i; 707 vis[i] = true; 708 if (y == 2) dfs(a, x + 1, 0, vis,list); 709 else dfs(a, x, y + 1, vis,list); 710 a[x, y] = 0; //回溯 711 vis[i] = false; //回溯 712 } 713 } 714 } 715 716 private void button13_Click(object sender, EventArgs e) //非递归回溯:在方阵里填数,使相邻数相加为质数 717 { 718 for (int i = 1; i <= N; i++) 719 { 720 b[i] = 1; //设置初值 721 } 722 List<int> list = new List<int>(); 723 find(list); 724 MessageBox.Show((list.Count/10).ToString(), "结果组数", MessageBoxButtons.OK); //结果组数 725 string s = ""; 726 for (int i = 0; i < list.Count; i++) 727 { 728 s += list[i].ToString() + "、"; 729 } 730 MessageBox.Show(s, "所有结果", MessageBoxButtons.OK); 731 } 732 733 private void write(int[] a, List<int> list) //将数组a转为list 734 { 735 // List<int> list = new List<int>(); 736 for (int i = 0; i < a.Length; i++) 737 { 738 list.Add(a[i]); 739 } 740 string s = ""; 741 for (int i = 0; i < list.Count; i++) 742 { 743 s += list[i].ToString() + "、"; 744 } 745 // MessageBox.Show((list.Count/10).ToString(), "结果", MessageBoxButtons.OK); 746 } 747 748 private int selectNum(int start) //选择下一个要试探填写的数,返回0表示该数已被选择 749 { 750 int j; 751 for (j = start; j <= N; j++) 752 { 753 if (b[j]==1) return j; 754 } 755 return 0; 756 } 757 758 private int check(int pos) //检测当前位置放入的数是否合理 759 { 760 int i, j; 761 if (pos < 0) return 0; 762 for (i = 0; (j = checkMatrix[pos,i]) >= 0; i++) 763 { 764 if (!isprime(a[pos] + a[j])) //i是表示checkMatrix矩阵中pos行第i个数(现总共为3个),j就是该数的实际值,即需要对比的位置 765 { 766 return 0; 767 } 768 } 769 return 1; 770 } 771 772 private int extend(int pos) //扩展至下一位置 773 { 774 a[++pos] = selectNum(1); //a[pos]即选中的数,因此要把b[a[pos]]置0,表示该数已被选用 775 b[a[pos]] = 0; 776 return pos; //返回下一个位置 777 } 778 private int change(int pos) //改变位置,即回溯至前面的位置 779 { 780 int j = selectNum(a[pos] + 1); 781 while (pos >= 0 && (j = selectNum(a[pos] + 1)) == 0) //j==0说明所选的数不可用,返回上一个位置pos-1位置 782 { 783 b[a[pos--]] = 1; 784 } 785 if (pos < 0) return -1; 786 b[a[pos]] = 1; 787 a[pos] = j; 788 b[j] = 0; 789 return pos; 790 } 791 private void find(List<int> list) //遍历寻找可行解 792 { 793 int ok = 1, pos = 0; 794 a[pos] = 1; b[a[pos]] = 0; 795 do 796 { 797 if (ok == 1) 798 { 799 if (pos == 8) 800 { 801 //MessageBox.Show("得到一个解", "结果", MessageBoxButtons.OK); 802 write(a,list); 803 pos = change(pos); 804 } 805 else 806 { 807 pos = extend(pos); //往下扩展 808 } 809 } 810 else 811 { 812 pos = change(pos); //如果ok==0,说明所选的数不可行,需要回溯 813 } 814 ok = check(pos); //检查该数是否符合要求,如果不符合要求则ok=0 815 } while (pos > 0); 816 } 817 818 private void button14_Click(object sender, EventArgs e) //分治法:比赛日程的安排问题 819 { 820 int k = 4; //规模,即有2^k个比赛选手 821 int N=(int)(Math.Pow(2,k)+1); //2的k次方加1 822 int[,] a = new int[N, N-1]; //用于存放结果,行代表选手编号,列代表比赛日 823 int twoml, twom, i, j, m; 824 m = 1; 825 twoml = 1; 826 a[1, 1] = 2; 827 a[2, 1] = 1; //预设两位选手的比赛日程 828 while (m < k) 829 { 830 m++; //分治规模,从小到大,m的最大值为k 831 twoml += twoml; //twoml的值随着m的扩大,依次从2、4、8、16……2^(k-1),等于规模数(选手总数)的一半 832 twom = 2 * twoml; //twom的值为4、8、16……2^k,等于规模数(选手总数) 833 /*以下填写左下角*/ 834 for (i = twoml + 1; i <= twom; i++) //填写左下角,i的值从选手编号的后半第1个数开始 835 { 836 837 for (j = 1; j <= twoml - 1; j++) //j的值,即列值(比赛日),从1到规模数一半减1 838 { 839 840 a[i,j] = a[i - twoml,j] + twoml; //等于上半对应的值加上规模数的一半 841 } 842 } 843 /*以下填写右上角*/ 844 a[1,twoml] = twoml + 1; //填写日程表右上角第一列第1个数,即规模数后半的第1个数 845 for (i = 2; i <= twoml; i++) 846 { 847 a[i,twoml] = a[i - 1, twoml] + 1; //右上角第一列,第i行等于其上方数加1 848 } 849 850 for (j = twoml + 1; j < twom; j++) //右上角第2列(twoml+1)至最后一列(twom-1) 851 { 852 for (i = 1; i < twoml; i++) //右上角第1行至第twoml-1行 853 { 854 a[i, j] = a[i + 1, j - 1]; //等于左侧一列左下方的选手编号 855 } 856 a[twoml, j] = a[1, j - 1]; //右上角每列的最后一行的值等于前一列第一个值 857 } 858 /*以下填写右下角*/ 859 for (j = twoml; j < twom; j++) //j为列编号,从规模数一半开始 860 { 861 for (i = 1; i <= twoml; i++) //i为选手编号,右下角只填写前一半选手编号 862 { 863 a[a[i, j], j] = i; //a[i,j]代表右上角同一列上第i行的数,按照同列上的第一行的数对应的行开始填数1、2、3…… 864 } 865 } 866 } 867 toDatagrid2(a); 868 } 869 870 private void button15_Click(object sender, EventArgs e) //贪心法多机调度问题 871 { 872 int[] s = { 16, 14, 6, 5, 4, 3,2 }; //各任务耗时,按从长到短排序 873 int n = s.Length; //任务总数量 874 int m = 3; //机器数量,小于n 875 int [] d=new int [m]; //m台机器的时间数组 876 for (int i = 0; i < m; i++) 877 { 878 d[i] = s[i]; //初始赋值,先选前面最大三个数 879 } 880 for (int i = m; i < n; i++) 881 { 882 d[min(d)] += s[i]; //把任务分配给d数组中值最小的数 883 } 884 885 pa(d, "结果"); 886 } 887 888 private int min(int[] a) //返回数组中最小数对应的下标 889 { 890 int t = 0; 891 for (int i = 1; i < a.Length; i++) 892 { 893 if (a[t] > a[i]) 894 { 895 t = i; 896 } 897 } 898 return t; 899 } 900 901 private void button1_Click(object sender, EventArgs e) //马的遍历问题,主函数 902 { 903 int sx, sy, i, j, step=0, no, start; 904 List <int [,]> list=new List<int[,]>(); 905 906 for (sx = 0; sx < 8; sx++) 907 { 908 for (sy = 0; sy < 8; sy++) 909 { 910 start = 0; 911 do 912 { 913 for (i = 0; i < 8; i++) 914 { 915 for (j = 0; j < 8; j++) 916 { 917 board[i, j] = 0; //清棋盘,全部置0 918 } 919 } 920 i = sx; 921 j = sy; 922 for (step = 1; step <= 64; step++) //step代表第几步,第1步为起始位置 923 { 924 if ((no = next(i, j, start)) == -1) break; //无出口,结束循环,no是下一步最少出口的着数 925 i += deltai[no]; 926 j += deltaj[no]; 927 board[i, j] = step; 928 } 929 if (step > 64) //找到一个可行解 930 { 931 //将可行解转移到kf,然后绑定到list;不能直接用list.Add(board),因为后面的数组会覆盖前面的数组 932 int[,] kf = new int [8,8]; 933 for (int ii = 0; ii < 8; ii++) 934 { 935 for (int jj = 0; jj < 8; jj++) 936 { 937 kf[ii,jj] = board[ii,jj]; 938 } 939 } 940 list.Add(kf); 941 break; 942 } 943 start++; 944 } while (step <= 64); 945 } 946 } 947 for (int ff = 0; ff < list.Count; ff++) //逐个显示可行解,一共64个 948 { 949 MessageBox.Show(ff.ToString(), "序号", MessageBoxButtons.OK); 950 toDatagrid2(list[ff]); 951 } 952 } 953 954 private int exitn(int i, int j, int s, int[] a) //求(i,j)的出口数,s为着数 955 { 956 int i1, j1, k, count; 957 for (count = k = 0; k < 8; k++) 958 { 959 i1 = i + deltai[k]; 960 j1 = j + deltaj[k]; 961 if (i1 >= 0 && i1 < 8 && j1 >= 0 && j1 < 8 && board[i1, j1] == 0) //下一着的位置须符合不出界、且是马尚未走过的位置 962 { 963 a[count++] = k; 964 } 965 } 966 return count; 967 } 968 private int next(int i, int j, int s) //选下一出口,即求出坐标为(i,j)、起始着数为s的下一出口数量最少的着数 969 { 970 int m, k, kk=0, min, temp; 971 int[] a = new int[8]; 972 int[] b = new int[8]; 973 m = exitn(i, j, s, a); //坐标为(i,j),着数为s,的马的出口数量, a存放着可行着数 974 if (m == 0) return -1; //无出口,返回-1 975 for (min = 9, k = 0; k < m; k++) //寻找可行着数中,下一出口最小的着数 976 { 977 temp = exitn(i + deltai[a[k]], j + deltaj[a[k]], s, b); 978 if (temp < min) 979 { 980 min = temp; 981 kk = a[k]; 982 } 983 } 984 return kk; 985 } 986 987 private void button16_Click(object sender, EventArgs e) //贪心法装箱问题 988 { 989 int[] ele = {60,45,35,20,20,20 }; //物品体积 990 int [] box=new int [ele.Length]; //准备箱子 991 string[] list = new string[ele.Length]; 992 string s = ""; 993 int boxcount = 0; 994 for (int i = 0; i < ele.Length; i++) 995 { 996 box[i] = 100; //每个箱子容量为100 997 } 998 for (int i = 0; i < ele.Length; i++) 999 { 1000 for (int j = 0; j < box.Length; j++) 1001 { 1002 if (ele[i] < box[j]) 1003 { 1004 box[j] -= ele[i]; //放入箱子 1005 list[j] += (i+1).ToString() + "、"; //记录每个箱子装哪些物品,物品编号从1开始 1006 break; 1007 } 1008 } 1009 } 1010 for (int i = 0; i < ele.Length; i++) //显示结果 1011 { 1012 if (list[i] != null) 1013 { 1014 s += list[i].Substring(0,list[i].Length-1) + ";"; 1015 boxcount++; 1016 } 1017 } 1018 MessageBox.Show(boxcount.ToString(), "使用的箱子数量", MessageBoxButtons.OK); 1019 MessageBox.Show(s, "每个箱子装入的物品编号", MessageBoxButtons.OK); 1020 } 1021 1022 1023 }
浙公网安备 33010602011771号