1. 程式人生 > 其它 >軟體設計師考試經典基礎演算法(分治法、回溯法、動態規劃法、貪心法、堆排序、歸併排序)含有詳細註釋

軟體設計師考試經典基礎演算法(分治法、回溯法、動態規劃法、貪心法、堆排序、歸併排序)含有詳細註釋

所有程式碼和註釋都是本人逐個敲出來,參考了軟體設計師考試指導教材裡的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 }