Thi HSG Quốc gia 2010
CCKLK: Dãy con chung không liền kề dài nhất
Cho dãy số nguyên dương x = (x1, x2, ..., xn). Dãy y = (xi1, xi2, ..., xik) được gọi là dãy con không liền kề của
dãy x nếu 1  i1 < i21 < ... < ik1  n. Cho 2 dãy số nguyên a gồm n phần tử và b gồm m phần tử. Xác
định chiều dài k của dãy con chung không liền kề dài nhất của a và b. 2  m, n  1000, 1 ai, bi  10000.
CCKLK.INP              CCKLK.OUT            Giải thích
5   6                  2                    CCKLK.INP: Dòng đầu: n m. Từ dòng thứ hai trở đi: Dãy số a,
1 5 3 8 2                                   tiếp đến là dãy số b.
2 1 3 4 2 6                                 CCKLK.OUT: k.

Thuật toán
Quy hoạch động.
Gọi d(i,j) là đáp số của bài toán khi xét hai dãy a[1..i] và b[1..j]. Ta có:
        Nếu i ≤ 0 thì ta quy ước d(i,j) = 0,
        Nếu a[i] = b[j] thì d(i,j) = d(i−2,j−2),
        Nếu a[i] ≠ b[j] thì d(i,j) = max { d(i−1,j), d(i,j−1) }.
Để cài đặt ta dùng 3 mảng một chiều x, y và z với ý nghĩa x[j] = d(i−2,j), y[j] = d(i−1,j) và z[j] = d(i,j). Khi
đó hệ thức trên được viết là:
        Nếu i ≤ 0 thì ta quy ước d(i,j) = 0,
        Nếu a[i] = b[j] thì d(i,j) = d(i−2,j−2) ứng với z[j] = x[j−2],
        Nếu a[i] ≠ b[j] thì d(i,j) = max { d(i−1,j), d(i,j−1) } ứng với z[j] = max { y[j], z[j−1] }.
Muốn tránh các phép copy dữ liệu từ y sang x; từ z sang y và từ x sang z ta chỉ cần tráo đổi các con trỏ
mảng.
Độ phức tạp
O(n.m)
Chương trình Pascal
         (* CCKLK.PAS
         k: chieu dai day con chung khong lien ke dai nhat
         cua hai day so nguyen duong a[1..n], b[1..m]
         *)
         const fn = 'ccklk.inp'; gn = 'ccklk.out';
                bl = #32; nl = #13#10; mn = 1001;
         type int = integer;
              mi1 = array[0..mn] of int;
         var
         n, m: int;
         a, b: mi1;

         function Max(a,b: int): int;
          begin if a >= b then Max := a else Max := b; end;

         procedure Doc;
          var i: int;
              f: text;
          begin
           assign(f,fn); reset(f);
           read(f,n, m);
writeln(n,bl,m);
  for i := 1 to n do   read(f,a[i]);
  for i := 1 to m do   read(f,b[i]);
  write(nl, 'a: ');
  for i := 1 to n do   write(a[i],bl);
  write(nl, 'b: ');
  for i := 1 to n do   write(b[i],bl);
  close(f);
end;

procedure Ghi(k: int);
 var g: text;
 begin
  assign(g,gn); rewrite(g);
  writeln(g,k); close(g);
end;

(*
  d(i,j) = dap so cua bai toan voi a[1..i], b[1..j]
  d(i,j) = d(i-2,j-2) + 1, if a[i] = b[j]
         = Max begin d(i,j-1), d(i-1,j) end;, elsewhere
         = 0, if i < 1 || j < 1
*)

function QHD: int;
 var i, j, v: int;
      c: array[1..3] of mi1;
      x, y, z, t: int;
begin
     x := 1; y := 2; z := 3;
     { Init i = 0 }
     fillchar(c[x],sizeof(c[x]), 0);
     { Init i = 1 }
     c[y][0] := 0; v := 0;
     for j := 1 to m do
      begin
       if (a[1] = b[j]) then v := 1;
       c[y][j] := v;
      end;
     v := 0;
     for i := 2 to n do
     begin
       c[z][0] := 0;
       if (a[i] = b[1]) then v := 1;
       c[z][1] := v;
       for j := 2 to m do
          if (a[i] = b[j]) then c[z][j] := c[x][j-2]+1
          else c[z][j] := Max(c[z][j-1],c[y][j]);
          t := x; x := y; y := z; z := t;
     end;
     QHD := c[y][m];
end;

BEGIN
 Doc;
 Ghi(QHD);
 write(nl,' Fini ');
readln;
      END.
Chương trình CPP
      /* DevC++ CCKLK.CPP
      k: chieu dai day con chung khong lien ke dai nhat
      cua hai day so nguyen duong a[1..n], b[1..m]
      */
      #include <fstream>
      #include <iostream>
      using namespace std;
      // D A T A A N D V A R I A B L E
      const char * fn = "ccklk.inp";
      const char * gn = "ccklk.out";
      const int mn = 1001;
      int n, m;
      int a[mn],b[mn];
      // P R O T O T Y P E S
      void Doc();
      int QHD(); // Quy hoach dong
      void Ghi(int);
      int Max(int,int);

      // I M P L E M E N T A T I O N
      int main() {
           Doc();
           Ghi(QHD());
           cout << endl << " Fini ";
           cin.get();
           return 0;
      }

      int Max(int a, int b) { return (a >= b) ? a : b; }

      void Doc() {
        int i;
        ifstream f(fn);
        f >> n >> m;
        cout << endl << n << " "   << m;
        for (i = 1; i <= n; ++i)   f >> a[i];
        for (i = 1; i <= m; ++i)   f >> b[i];
        cout << endl << "a: ";
        for (i = 1; i <= n; ++i)   cout << a[i] << " ";
        cout << endl << "b: ";
        for (i = 1; i <= m; ++i)   cout << b[i] << " ";
        f.close();
      }

      void Ghi(int k) {
         ofstream g(gn);
         g << k;
         g.close();
      }
      /*
         d(i,j) = dap so cua bai toan voi a[1..i], b[1..j]
         d(i,j) = d(i-2,j-2) + 1, if a[i] = b[j]
                = Max { d(i,j-1), d(i-1,j) }, elsewhere
= 0, if i < 1 || j < 1
        */

        int QHD() {
            int *x, *y, *z, *t;
            int i, j , m1 = m+1, v ;
            x = new int[m1];
            y = new int[m1];
            z = new int[m1];
            // Init i = 0
            memset(x,0,sizeof(int)*m1);
            // Init i = 1
            y[0] = 0; v = 0;
            for (j = 1; j <= m; ++j) {
              if (a[1] == b[j]) v = 1;
              y[j] = v;
            }
            v = 0;
            for (i = 2; i <= n; ++i) {
              z[0] = 0;
              if (a[i]==b[1]) v = 1;
              z[1] = v;
              for (j = 2; j <= m; ++j)
                z[j] = (a[i] == b[j]) ? x[j-2]+1 : Max(z[j-1],y[j]);
              t = x; x = y; y = z; z = t;
            }
            v = y[m];
            delete x; delete y; delete z;
            return v;
        }

Ổn định
Cho đồ thị có hướng gồm n đỉnh và m cung (u,v). Cho trước đỉnh s. Một đỉnh d ≠ s được gọi là ổn định đối
với s nếu có ít nhất hai đường đi ngắn nhất từ s tới d. Hãy tính k là số lượng đỉnh ổn định đối với đỉnh s.
2  n  10000, 1  m  50000.


ONDINH.INP           ONDINH.OUT          Giải thích
6 7 1                2                   ONDINH.INP: Dòng đầu: n m s. Từ dòng thứ hai trở đi:m cung
1 2                                      dạng u v. Có thể có các cung trùng nhau (dư thừa).
1 4                                      ONDINH.OUT: k.
2 3                                      Thí dụ cho biết: có 3 đỉnh ổn định đối với đỉnh 1 là các đỉnh 5
2 5                                      và 6.
4 5                                      1 → 2 → 5; 1 → 4→ 5;
2 3                                      1 → 2 → 5→ 6; 1 → 4→ 5→ 6.
5 6


Thuật toán
Dùng một biến thể của thuật toán Dijkstra.


        // Ondinh.CPP
        // HSG 2010

        #include <string.h>
#include <fstream>
#include <iostream>
//#include <mem.h>

using namespace std;

// D A T A   A N D    V A R I A B L E S

char * fn = "ondinh.inp";
char * gn = "ondinh.out";
const int mn = 10001;
const int mm = 50001;
typedef struct { int a, b; } cung;
cung c[mm];
int len[mn]; // len[i] chieu dai s => i
char mark[mn]; // mark[i] danh dau dinh i:
 // Chua xet 0; Co trong hang doi q 1; Da xu li 2
int d[mn]; // d[i] so luong duong ngan nhat s => i
int q[mn]; // hang doi
int n, m, s ; // so dinh n, so cung m, dinh xuat phat s
// P R O T O T Y P E S
int main();
void Doc();
int XuLi();
int Min(int, int);
int BinSearch(cung [], int, int, int);
int Sanh(int,int,int,int);
void Ghi();

int main(){
    Doc(); XuLi(); Ghi();
    cout << endl << " Fini"; cin.get();
    return 0;
}

int Min(int a, int b) { return (a <= b) ? a : b; }

void Ghi() {
     int i, k = 0;
     for (i = 1; i <= n; ++i)
       if (d[i] > 1) ++k;
     ofstream g(gn);
     g << k;
     g.close();
}

int XuLi() {
 int i, j, k, v, r;
 v = 0; r = 0;
 memset(mark,0,sizeof(mark));
 memset(d,0,sizeof(d));
 len[s] = 0; d[s] = 1;
 q[++v] = s;
 while (r < v) { // 1
     i = q[++r]; cout << endl << " Xet dinh " << i;
     mark[i] = 2;
     for (k = BinSearch(c,m,i,0); c[k].a == i; ++k) { // 2
j = c[k].b; // xet cac dinh j ke dinh i
        if (mark[j] == 0) {
          len[j] = len[i]+1; mark[j] = 1;
          d[j] = d[i]; q[++v] = j;
        }
        else if (mark[j] == 1) {
          if (len[i]+1 == len[j]) d[j]++;
        }
      } // 2
 } // 1
 for (i = 1; i <= n; ++i) cout << d[i] << " ";
}

int Sanh(int u1, int v1, int u2, int v2) {
   if (u1 < u2) return -1;
   if (u1 > u2) return 1;
   // u1 == u2
   if (v1 < v2) return -1;
   if (v1 > v2) return 1;
   return 0;
}

int BinSearch(cung c[], int k, int u, int v) {
    int d = 1, m;
    while (d < k) {
      m = (d + k) / 2;
      if (Sanh(c[m].a,c[m].b,u,v) < 0) d = m+1; else k = m;
    }
    return d;
}
void Doc() {
  int i, j, u, v, k = 0;
  ifstream f(fn);
  f >> n >> m >> s;
  cout << endl << n << " " << m << " " << s;
  // The first edge (u,v)
  f >> u >> v;
  ++k; c[k].a = u; c[k].b = v;
  for (i = 2; i <= m; ++i) {
    f >> u >> v;
    j = BinSearch(c,k,u,v);
    if (Sanh(c[j].a,c[j].b,u,v) != 0) {
      if (Sanh(c[j].a,c[j].b,u,v) < 0) { ++k; c[k].a = u; c[k].b
= v; }
      else {
         memmove(&c[j+1], &c[j], (k-j+1)*sizeof(cung));// <mem.h>
         c[j].a = u; c[j].b = v; ++k;
      }
    }
  }
  f.close();
  m = k;
}
// Ondinh.CPP Phuong an cu
// HSG 2010

#include <string.h>
#include <fstream>
#include <iostream>
//#include <mem.h>

using namespace std;

// D A T A   A N D    V A R I A B L E S

char * fn = "ondinh.inp";
char * gn = "ondinh.out";
const int mn = 10001;
const int mm = 50001;
typedef struct { int a, b; } cung;
cung c[mm];
int p[mn];
char d[mn];
int s[mn];
int n, m, x; // so dinh n, so cung m, dinh xuat phat x
// P R O T O T Y P E S
int main();
void Doc();
int XuLi();
int Minp();
int Min(int, int);
int BinSearch(cung [], int, int, int);
int Sanh(int,int,int,int);
void Ghi();

int main(){
    Doc(); XuLi(); Ghi();
    cout << endl << " Fini"; cin.get();
    return 0;
}

int Min(int a, int b) { return (a <= b) ? a : b; }

int Minp() {
    int i, imin = 0;
    for (i = 1; i <= n; ++i)
      if (!d[i] && p[i] < p[imin]) imin = i;
    d[imin] = 1;
    return imin;
}

void Ghi() {
     int i, k = 0;
     for (i = 1; i <= n; ++i)
       if (s[i] > 1) ++k;
     ofstream g(gn);
     g << k;
     g.close();
}
int XuLi() {
  int i, imin, j, k;
  memset(d,0,sizeof(d));
  for (i = 2; i <= n; ++i) s[i] = 0;
  s[x] = 1;
  p[0] = n+2; p[x] = 0;
  for (i = 2; i <= n; ++i) p[i] = n+1;
  for (i = 1; i <= n; ++i) {
      imin = Minp();
      for (k = BinSearch(c,m,imin,0); c[k].a == imin; ++k) {
          j = c[k].b;
          if (!d[j]) {
             if (p[imin]+1 < p[j]) { p[j] = p[imin]+1; s[j]    =
s[imin]; }
            else if (p[imin]+1 == p[j]) ++s[j];
        }
      }
  }
}

int Sanh(int u1, int v1, int u2, int v2) {
   if (u1 < u2) return -1;
   if (u1 > u2) return 1;
   // u1 == u2
   if (v1 < v2) return -1;
   if (v1 > v2) return 1;
   return 0;
}

int BinSearch(cung c[], int k, int u, int v) {
    int d = 1, m;
    while (d < k) {
      m = (d + k) / 2;
      if (Sanh(c[m].a,c[m].b,u,v) < 0) d = m+1; else k = m;
    }
    return d;
}
void Doc() {
  int i, j, u, v, k = 0;
  ifstream f(fn);
  f >> n >> m >> x;
  // The first edge (u,v)
  f >> u >> v;
  ++k; c[k].a = u; c[k].b = v;
  for (i = 2; i <= m; ++i) {
    f >> u >> v;
    j = BinSearch(c,k,u,v);
    if (Sanh(c[j].a,c[j].b,u,v) != 0) {
      if (Sanh(c[j].a,c[j].b,u,v) < 0) { ++k; c[k].a = u; c[k].b
= v; }
      else {
        memmove(&c[j+1], &c[j], (k-j+1)*sizeof(cung));// <mem.h>
        c[j].a = u; c[j].b = v; ++k;
      }
    }
  }
  f.close();
m = k;
          }


Mã số thuế
Xét tập S gồm tất cả các số 1..n trong hệ 36, 36  n  1016. Cho số m: 3  m  70. Xét dãy số nguyên 1 < c1
< c2 < ... < ck < 36, k = (m1)/2 , x là số nguyên lớn nhất không vượt quá x.
Chọn các số chứa các chữ số < c1 cấp cho 2 nhóm 1 và 2 rồi xóa các số này. Chọn các số chứa các chữ số
< c2 cấp cho 2 nhóm 3, 4.... Các số còn lại cấp cho 1 hoặc 2 nhóm cuối.
Nhóm le: từ nhỏ, nhóm chẵn: từ lớn.
Cho các số hệ 10: n, m, ci, p và q. Xác định mã (hệ 36) cấp cho ng thứ q nhóm p.
Thí dụ:
n = 50, m = 3, p = 2, q = 2, c1 = 16.                                         1d
Căt đoạn
Cho hình chữ nhật OABC, OA = n, OC = m, coi O là gốc tọa độ (0,0). Trong hình CN cho k đoạn thẳng
đứng. Tìm điểm P trên BA hoặc BC để đoạn OP cắt nhiều đoạn nhất.


Đoạn khác nhau
Cho dãy a gồm n số nguyên dương. Một đoạn của dãy a, kí hiệu a[i..j] là dãy gồm các phần tử đứng liên
tiếp nhau trong dãy a, kể từ phần tử ai đến phần tử aj, a[i..j] = (ai, ai+1,...,aj-1, aj), 1  i <= j  n. Hãy tìm
đoạn dài nhất gồm các phần tử đôi một khác nhau.
1  n, ai  100000.
Dữ liệu vào: Tệp văn bản diff.inp
         Dòng đầu tiên: số n.
         Từ dòng thứ hai trở đi: dãy số a.
Dữ liệu ra: Tệp văn bản diff.out chứa 2 số:
         imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
         dmax  số phần tử của doạn dài nhất.
Các số trên cùng dòng cách nhau qua dấu cách.


                        diff.inp             diff.out        Giải thích
                                                             Tính từ phần tử thứ 3 có cả thảy
                        10                   3
                                                             8 số đôi một khác nhau là
                        5 2 4 1 5            8
                        3 2 7 6 9                                 4 1 5 3 2 7 6 9


Thuật toán
Lần lượt đọc các phần tử ai của dãy a và đánh dấu vị trí xuất hiện của ai trong dãy thông qua mảng p, p[ai] =
i. Với thí dụ đã cho, sau khi đọc và xử lý xong dãy a ta phải thu được


                   i      1      2       3       4      5         6       7   8      9      10
                   ai     5      2       4       1      5        3        2   7      6      9
                   p      4      2/7     6       3     1/5        9       8   0     10      0
p[2] = 2/7 cho biết số 2 lúc đầu xuất hiện tại vị trí 2 trong dãy a, sau đó
                xuất hiện tại vị trí 7 trong dãy a. p[8] = p[10] = 0 cho biết các số 8 và 10
                không xuất hiện trong dãy a.


Ta gọi p là dãy trỏ ngược hay dãy vị trí của dãy a. Ta xử lý từng đoạn d của ai như sau. Mỗi đoạn d sẽ bao
gồm một dãy liên tiếp các phần tử đôi một khác nhau tính từ chỉ số i đến j. Thí dụ trên cho ta lần lượt 3
đoạn sau:
       Đoạn thứ nhất d = a[1..4] = (5, 2, 4, 1), i = 1, j = 4,
       Đoạn thứ hai d = a[2..6] = (2, 4, 1, 5, 3), i = 2, j = 6,
       Đoạn thứ ba d = a[3..10] = (4, 1, 5, 3, 2, 7, 6, 9), i = 3, j = 10.
Mỗi đoạn d được xác định như sau: Mỗi khi gặp phần tử aj đầu tiên trùng với một phần tử trong dãy tính từ
i thì ta cắt ra được đoạn d = a[i..j1].
Với mỗi đoạn d[i..j] ta tính số phần tử của đoạn đó là ji+1 và cập nhật giá trị dmax. Để khởi trị cho đoạn
tiếp theo, ta đặt i = p[aj]+1. Chú ý rằng p[aj] là vị trí xuất hiện của giá trị lặp aj.
Độ phức tạp
O(n)
Chương trình Pascal
        (* diff.pas
           Tim doan dai nhat gom cac
           phan tu doi mot khac nhau
        *)

        const fn = 'diff.inp'; gn = 'diff.out';
              bl = #32; nl = #13#10;
              mn = 100001;
        var p: array[0..mn] of longint;
        n: longint;
        imax, dmax: longint;
        { imax - chi so dau tien cua doan dai nhat
          dmax - so phan tu cua doan dai nhat }
        f,g: text;

        procedure Run;
          var i, j, v, istart: longint;
        begin
          imax := 0; dmax := 0;
          fillchar(p,sizeof(p),0);
          assign(f,fn); reset(f);
          readln(f,n);
          read(f,v); { phan tu dau tien trong day }
          p[v] := 1; istart := 1;
          for i := 2 to n do
          begin
              read(f,v);
              if (p[v] >= istart) then
              begin
                if (i-istart > dmax)then
                begin
                    dmax := i-istart; imax := istart;
                end;
                istart := p[v]+1;
end;
             p[v] := i;
        end;
        close(f);
        i := n+1;
        if (i-istart > dmax) then
        begin
           dmax := i-istart; imax := istart;
        end;
        assign(g,gn); rewrite(g);
        write(g, imax, nl, dmax);
        close(g);
      end;

      BEGIN
       Run;
       write(nl, ' Fini '); readln;
      END.
Chương trình CPP
      /* DevC++: Diff.cpp
         Tim doan dai nhat gom cac
         phan tu doi mot khac nhau
      */

      #include <fstream>
      #include <iostream>

      using namespace std;

      // D A T A    A N D   V A R I A B L E S

      const char * fn = "diff.inp";
      const char * gn = "diff.out";
      const int mn = 100001;
      int p[mn]; // p[v] = i: noi xuat hien so v trong day
      int n;
      int imax; // chi so dau tien cua doan dai nhat
      int dmax; // so phan tu cua doan dai nhat

      // P R O T O T Y P E S

      int main();
      void Run();

      // I M P L E M E N T A T I O N

      int main() {
          Run();
          cout << endl;
          system("PAUSE");
          return EXIT_SUCCESS;
      }

      void Run() {
        int i, j, v, istart;
        imax = 0, dmax = 0;
memset(p,0,sizeof(p));
             ifstream f(fn);
             f >> n;
             f >> v; // v phan tu dau day
             p[v] = 1; istart = 1;
             for (i = 2; i <= n; ++i) {
                 f >> v;
                 if (p[v] >= istart) { // so v co trong doan istart..i
                      if (i-istart > dmax) {
                        dmax = i-istart; imax = istart;
                      }
                      istart = p[v]+1;
                 }
                 p[v] = i;
             }
             f.close();
             i = n+1;
             if (i-istart > dmax) {
               dmax = i-istart; imax = istart;
             }
             ofstream g(gn); g << imax << endl << dmax;
         }


Hoán vị 1k
Cho dãy a gồm n số nguyên dương đôi một khác nhau. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành
một hoán vị của dãy (1, 2 ,..., k), 1  n, ai  100000.
Dữ liệu vào: Tệp văn bản hv1k.inp
        Dòng đầu tiên: số n.
        Từ dòng thứ hai trở đi: dãy số a.
Dữ liệu ra: Tệp văn bản hv1k.out chứa 2 số:
        imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
        dmax  số phần tử của đoạn dài nhất.
Các số trên cùng dòng cách nhau qua dấu cách.


                     hv1k.inp              hv1k.out      Giải thích
                     10                    3             Tính từ phần tử thứ 3 có 6 số
                     5 2 4 1 5             6             liên tiếp tạo thành một hoán vị
                     3 2 6 9 8                           của 1..6 là
                                                                  4 1 5 3 2 6


Thuật toán
Kí hiệu a[i..j] là đoạn gồm các phần tử liên tiếp từ ai đến aj của dãy a và kí hiệu set(x) là tập chứa các phần
tử (khác nhau) của dãy x. Với thí dụ đã cho ta có, a[1..5] = (5, 2, 4, 1, 5) và do đó set(a[1..5]) = {1, 2, 4,
5}. Gọi p là dãy vị trí (trỏ ngược) của dãy a. Vì dãy a gồm các phần tử đôi một khác nhau nên p cũng chứa
các chỉ số đôi một khác nhau. Khi đó a chứa một đoạn là hoán vị của k số tự nhiên đầu tiên (1,2,...,k) khi và
chỉ khi tìm được hai chỉ số s và e thỏa đồng thời hai tính chất sau:
        es+1 = k, và
        set(a[s..e]) = {1, 2, ... , k}.
Trong thí dụ trên ta tìm được s = 3, e = 8, k = 6, a[3..8] = (4, 1, 5, 3, 2, 6), và do đó set(a[3..8]) = {1, 2, 3, 4,
5, 6}.
Xét dãy chỉ số 1, 2, ..., i thỏa tính chất  j: 1  j  i: p[j] ≠ 0.
Để ý rằng điều kiện p[j] ≠ 0 tương đương với điều kiện giá trị j của dãy a xuất hiện tại vị trí p[j]. Đặt s =
min p[1..i] = min {p[1], p[2], ... , p[i]} và e = max p[1..i] = max {p[1], p[2], ... , p[i]}. Ta thấy a chứa một
đoạn là hoán vị của dãy (1,2,...,i) khi và chỉ khi es+1 = i.
Độ phức tạp
O(n).
Chương trình Pascal
          (* --------------------------------------
            hv1k.pas
            Tim doan dai nhat trong day so
            doi mot khac nhau tao thanh mot
            hoan vi 1..k
          -----------------------------------------*)

          const fn = 'hv1k.inp'; gn = 'hv1k.out';
                mn = 1000002; nl = #13#10; bl = #32;
          var p: array[0..mn] of longint;
              n: longint;
              imax, dmax: longint;
              f,g: text;

          procedure Doc;
           var i, v: longint;
          begin
            assign(f,fn); reset(f);
            fillchar(p,sizeof(p),0);
            readln(f,n);
            for i := 1 to n do
            begin
               read(f,v); p[v] := i;
            end;
            close(f);
          end;

          function Min(a,b: longint): longint;
          begin
           if (a <= b) then Min := a else Min := b
          end;

          function Max(a,b: longint): longint;
          begin
           if (a >= b) then Max := a else Max := b
          end;

          procedure Hv;
           var i, pmin, pmax: longint;
          begin
           pmin := n+1; pmax := 0;
           imax := 0; dmax := 0;
           for i := 1 to n do
           begin
            if (p[i] = 0) then break;
pmin := Min(pmin,p[i]);
        pmax := Max(pmax,p[i]);
        if (pmax - pmin + 1 = i) then
        begin
            imax := pmin;
            dmax := pmax - pmin + 1;
        end;
       end;
      end;

      procedure Ghi;
      begin
        assign(g,gn); rewrite(g);
        writeln(g, imax,nl,dmax);
        close(g);
      end;

      procedure Run;
      begin
       Doc; Hv; Ghi;
      end;

      BEGIN
       Run;
       write(nl,' Fini '); readln;
      END.

Chương trình CPP
      /* --------------------------------------
        devCPP: hv1k.cpp
        Tim doan dai nhat trong day so
        doi mot khac nhau tao thanh mot
        hoan vi 1..k
      -----------------------------------------*/
      #include <fstream>
      #include <iostream>

      using namespace std;

      // D A T A   A N D   V A R I A B L E S

      const char * fn = "hv1k.inp";
      const char * gn = "hv1k.out";
      const int mn = 100002;
      int p[mn];
      int n;
      int imax, dmax;

      // P R O T O T Y P E S

      int main();
      void Doc();
      int Min(int, int);
      int Max(int, int);
      void Hv();
      void Ghi();
void Run();

        // I M P L E M E N T A T I O N

        int main() {
            Run();
            cout << endl;
            system("PAUSE");
            return EXIT_SUCCESS;
        }

        void Doc() {
          int i, v;
          ifstream f(fn);
          memset(p,0,sizeof(p));
          f >> n;
          for (i = 1; i <= n; ++i) {
            f >> v;
            p[v] = i;
          }
          f.close();
        }

        int Min(int a, int b) { return (a <= b) ? a : b; }

        int Max(int a, int b) { return (a >= b) ? a : b; }

        void Hv() {
          int i, pmin = n+1, pmax = 0;
          imax = 0; dmax = 0;
          for (i = 1; i <= n; ++i) {
           if (p[i] == 0) break;
            pmin = Min(pmin,p[i]);
            pmax = Max(pmax,p[i]);
            if (pmax - pmin + 1 == i) {
               imax = pmin;
               dmax = pmax - pmin + 1;
            }
           }
        }
        void Ghi() {
           ofstream g(gn);
           g << imax << endl << dmax;
           g.close();
        }

        void Run() {
          Doc();
          Hv();
          Ghi();
        }


Hoán vị sk
Cho dãy a gồm n số nguyên dương đôi một khác nhau. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành
một hoán vị của dãy số tự nhiên liên tiếp (s, s+1 ,..., k), 1  n, ai  100000.
Dữ liệu vào: Tệp văn bản hvsk.inp
        Dòng đầu tiên: số n.
        Từ dòng thứ hai trở đi: dãy số a.
Dữ liệu ra: Tệp văn bản hvsk.out chứa 2 số:
        imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
        dmax  số phần tử của đoạn dài nhất.
Các số trên cùng dòng cách nhau qua dấu cách.


                       hvsk.inp                  hvsk.out       Giải thích
                       7                         3              Tính từ phần tử thứ 3 trong dãy
                       12 1 4 7 8                5              a có 5 số liên tiếp tạo thành một
                       5 6                                      hoán vị của 4..8 là a[3..7]:
                                                                          4 7 8 5 6


Thuật toán
Gọi p là dãy trỏ ngược của dãy a. Vì dãy a gồm các phần tử đôi một khác nhau nên p cũng chứa các chỉ số
đôi một khác nhau. Khi đó a chứa một đoạn là hoán vị của dãy số tự nhiên liên tiếp s..k khi và chỉ khi tìm
được hai chỉ số i và j thỏa đồng thời hai tính chất sau:
        ji = ks, và
        set(a[i..j]) = {s, s+1, ... , k}.
Trong thí dụ trên ta tìm được i = 3, j = 7, s = 4, k = 8, a[3..7] = (4, 7, 8, 5, 6), và do đó set(a[3..7]) = {4, 5,
6, 7, 8}.
Khi đọc dữ liệu ta đồng thời xác định hai giá trị cận vmin và vmax của dãy a. Để ý rằng p[vmin..vmax]
gồm các đoạn toàn 0 và đoạn khác 0 đan xen nhau. Thí dụ trên cho ta


          ai      1        2        3        4       5      6       7        8     9      10        11   12
          p        2       0        0        3     6      7      4           5     0       0        0    1
                                              vmin = 1. vmax = 12


Khi duyệt p[vmin..vmax] ta xét 2 trạng thái 0 và 1 như sau:
Trạng thái 0: Duyệt đoạn p toàn 0, p[2..3] và p[9..11].
Nếu gặp p[i] > 0 thì khởi trị cho trạng thái duyệt đoạn khác 0, p[istart..]:
        Ghi nhận chỉ số đầu đoạn khác 0: istart = i,
        Khởi trị cac giá trị pmin = min p[istart..k1] và pmax = max p[istart..k1] cho đoạn này: pmin =
         pmax = p[i],
        Chuyển qua trạng thái 1.
Trạng thái 1: Duyệt đoạn p khác 0, p[1..1], p[4..8] và p[12..12].
Nếu p[i] > 0 thì cập nhật các chỉ số pmin và pmax. Kiểm tra đẳng thức pmaxpmin = iistart để cập nhật
chỉ số đầu tiên của đoạn hoán vị trong dãy a, imax và chiều dài của đoạn hoán vị, dmax.
Nếu p[i] = 0 thì kết thúc việc duyệt đoạn p[isstart..i1] này, chuyển qua trạng thái 0.
Độ phức tạp
O(n).
Chương trình Pascal

Chương trình CPP
       /* --------------------------------------
         hvsk.cpp
         Tim doan dai nhat trong day so
         doi mot khac nhau tao thanh mot
         hoan vi cua day so tu nhien lien tiep
         s, s+1,...,k
       -----------------------------------------*/
       #include <fstream>
       #include <iostream>

       using namespace std;

       // D A T A     A N D   V A R I A B L E S

       const char * fn = "hvsk.inp";
       const char * gn = "hvsk.out";
       const int mn = 100002;
       int p[mn];
       int pmin, pmax;
       int vmin, vmax;
       int n;
       int imax, dmax;

       // P R O T O T Y P E S

       int main();
       void Doc();
       int Min(int, int);
       int Max(int, int);
       void Hv(); // Sliding window
       void Ghi();
       void Run();

       // I M P L E M E N T A T I O N

       int main() {
           Run();
           cout << endl;
           system("PAUSE");
           return EXIT_SUCCESS;
       }

       void Doc() {
         int i, v;
         ifstream f(fn);
         memset(p,0,sizeof(p));
         vmin = mn; vmax = 0;
         f >> n;
         for (i = 1; i <= n; ++i) {
           f >> v;
           p[v] = i;
           vmin = Min(vmin,v); vmax = Max(vmax,v);
}
            f.close();
        }

        int Min(int a, int b) { return (a <= b) ? a : b; }

        int Max(int a, int b) { return (a >= b) ? a : b; }

        void Hv() {
          int i, istart, q = 0; // trang thai q
          dmax = 0; ++vmax;
          for (i = vmin; i <= vmax; ++i) {
            switch(q) {
             case 0: // duyet doan toan 0
                     if (p[i] > 0) { // Khoi tri khi gap so khac 0
                        pmin = pmax = p[i];
                        istart = i; q = 1;
                     }
                     break;
              case 1: // duyet doan khac 0
                     if (p[i] > 0) {
                        pmin = Min(pmin,p[i]);
                        pmax = Max(pmax,p[i]);
                        if (pmax-pmin == i-istart && pmax-pmin+1 > dmax){
                            dmax = pmax-pmin+1;
                            imax = pmin;
                        }
                     }
                     else q = 0;
                     break;
              } // end switch
            } // end for
        }

        void Ghi() {
          ofstream g(gn);
          g << imax << endl << dmax;
          g.close();
        }

        void Run() {
          Doc();
          Hv();
          Ghi();
        }


Hoán vị 1k dài nhất

                                                                       IOIcamp Marathon 2005-2006

Cho dãy a gồm n số nguyên dương. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành một hoán vị của dãy
số tự nhiên 1..k; 1  n, ai  100000.
Dữ liệu vào: Tệp văn bản hv1kmax.inp
       Dòng đầu tiên: số n.
   Từ dòng thứ hai trở đi: dãy số a.
Dữ liệu ra: Tệp văn bản hv1kmax.out chứa 2 số:
       imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
       dmax  số phần tử của đoạn dài nhất.
Các số trên cùng dòng cách nhau qua dấu cách.



                 hv1kmax.inp         hv1kmax.out       Giải thích
                 10                  3                 Tính từ phần tử thứ 3 trong dãy
                 12 1 4 1 5          5                 a có 5 số liên tiếp tạo thành một
                 3 2 3 1 2                             hoán vị của 1..5 là a[3..7]:
                                                                 4 1 5 3 2



Thuật toán
Duyệt dãy, dựa theo bài diff, xác định từng đoạn ứng viên a[d..c] chứa tối đa các phần tử liên tiếp nhau
trong dãy a và đôi một khác nhau. Với mỗi đoạn ứng viên, dựa theo bài Hv1k, gọi thủ tục Hv(d,c) xác định
và cập nhật đoạn dài nhất trong a[d..c] tạo thành một hoán vị của dãy số tự nhiên 1..k.
Độ phức tạp
Chương trình Pascal
        (* hv1kmax.pas: Tim doan dai nhat gom cac
          phan tu tao thanh mot hoan vi cua 1..k *)

        const fn = 'hv1kmax.inp'; gn = 'hv1kmax.out';
              mn = 100002; bl = #32; nl = #13#10;

        var p: array[0..mn] of longint;
        { p[v] - noi xuat hien gia tri v trong day }
        n: longint;
        imax, dmax: longint;
        f, g: text;

        function Min(a,b: longint): longint;
        begin if (a <= b) then Min := a else Min := b; end;

        function Max(a,b: longint): longint;
        begin if (a >= b) then Max := a else Max := b; end;

        procedure Hv(d,c: longint);
         var i, pmin, pmax: longint;
        begin
          pmin := c+1; pmax := d-1;
          for i := 1 to c-d+1 do
          begin
            if (p[i] < d ) or (p[i] > c) then break;
            pmin := Min(pmin,p[i]);
            pmax := Max(pmax,p[i]);
            if (i > dmax) then
            begin
              if (pmax - pmin + 1 = i) then
begin
                imax := pmin;
                dmax := i;
            end
           end
        end;
      end;

      procedure Ghi;
      begin
        assign(g,gn); rewrite(g);
        writeln(g,imax, nl, dmax); close(g);
      end;

      procedure Run;
       var i, v , istart: longint;
      begin
        fillchar(p,sizeof(p),0);
        imax := 0; dmax := 0;
        assign(f,fn); reset(f);
        readln(f,n);
        read(f,v); { v – phan tu dau day a }
        p[v] := 1; istart := 1; { chi so dau doan }
        for i := 2 to n do
        begin
           read(f,v);
           if (p[v] >= istart) then { gap phan tu trung lap }
           begin
            if (i-istart > dmax) then Hv(istart, i-1);
            istart := p[v]+1; { Khoi tri dau doan moi }
           end;
           p[v] := i;
        end;
        close(f);
        if (n+1-istart > dmax) then Hv(istart,n);
        Ghi;
      end;

      BEGIN
       Run;
       write(nl,' Fini '); readln;
      END.

Chương trình CPP

      /* DevC++: hv1kmax.cpp
         Tim doan dai nhat gom cac
         phan tu tao thanh mot hoan vi cua 1..k
      */
      #include <fstream>
      #include <iostream>

      using namespace std;

      // D A T A   A N D   V A R I A B L E S
const char * fn = "hv1kmax.inp";
const char * gn = "hv1kmax.out";
const int mn = 100002;
int p[mn]; // p[v] - noi xuat hien gia tri v trong day
int n;
int imax, dmax;

// P R O T O T Y P E S

int main();
void Run();
int Min(int, int);
int Max(int, int);
void Hv(int, int); // Sliding window
void Ghi();

// I M P L E M E N T A T I O N

int main() {
    Run();
    cout << endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}

int Min(int a, int b) { return (a <= b) ? a : b; }

int Max(int a, int b) { return (a >= b) ? a : b; }

// tim hoan vi 1..n trong doan
// gom cac phan tu khac nhau doi mot
void Hv(int d, int c) {
  int i, pmin = c+1, pmax = d-1;
  int n = c-d+1;
  for (i = 1; i <= n; ++i) {
    if (p[i] < d || p[i] > c) break;
    pmin = Min(pmin,p[i]);
    pmax = Max(pmax,p[i]);
    if (i > dmax) {
      if (pmax - pmin + 1 == i) {
         imax = pmin;
         dmax = i;
      }
    }
   }
}

void Ghi() {
  ofstream g(gn);
  g << imax << endl << dmax;
  g.close();
}

void Run() {
  int i, v , istart;
  memset(p,0,sizeof(p));
  imax = 0; dmax = 0;
ifstream f(fn);
            f >> n;
            f >> v; p[v] = 1; istart = 1;
            for (i = 2; i <= n; ++i) {
                f >> v;
                if (p[v] >= istart) {
                   if (i-istart > dmax) Hv(istart, i-1);
                   istart = p[v]+1;
                }
                p[v] = i;
            }
            f.close();
            if (n+1-istart > dmax) Hv(istart,n);
            Ghi();
          }

Hoán vị sk dài nhất
Cho dãy a gồm n số nguyên dương. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành một hoán vị của dãy
số nguyên dương liên tiếp s, s+1,...,k; 1  n, ai  100000.
Dữ liệu vào: Tệp văn bản hvskmax.inp
        Dòng đầu tiên: số n.
        Từ dòng thứ hai trở đi: dãy số a.
Dữ liệu ra: Tệp văn bản hvskmax.out chứa 2 số:
        imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a
        dmax  số phần tử của đoạn dài nhất.
Các số trên cùng dòng cách nhau qua dấu cách.



                  hvskmax.inp           hvskmax.out        Giải thích
                  10                    3                  Tính từ phần tử thứ 3 trong dãy
                  5 1 9 7 5             5                  a có 5 số liên tiếp tạo thành một
                  8 6 3 9 2                                hoán vị của 5..9 là a[3..7]:
                                                                     9 7 5 8 6


Thuật toán
Pha 1. Đọc dãy a, với mỗi ai ghi nhận p[ai] = i là vị trí xuât hiện giá trị ai. Gọi vmin và vmax lần lượt là giá
trị min và max của dãy a.
Pha 2. Duỵệt dãy giá trị i = vmin..vmax tạo thành một đoạn d gồm các giá trị xuất hiện liên tiếp trong dãy
a, xác định các giá trị pmin và pmax là vị trí xuất hiện đầu tiên và cuối cùng trong đoạn d. Đoạn d là một
hoán vị của dãy số nguyên dương liên tiếp i, i+1,...,s khi và chỉ khi pmaxpmin+1 = si.
Độ phức tạp
Chương trình Pascal
Chương trình CPP

         /* DevC++: hv1kmax.cpp
            Tim doan dai nhat gom cac
            phan tu tao thanh mot hoan vi cua s..k
         */
#include <fstream>
#include <iostream>

using namespace std;

// D A T A   A N D   V A R I A B L E S

const char * fn = "hvskmax.inp";
const char * gn = "hvskmax.out";
const int mn = 100002;
int p[mn]; // p[v] - noi xuat hien gia tri v trong day
int a[mn];
int n;
int imax, dmax;

// P R O T O T Y P E S

int main();
void Run();
int Min(int, int);
int Max(int, int);
void Hv(int, int); // Sliding window
void Ghi();

// I M P L E M E N T A T I O N

int main() {
    Run();
    cout << endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}

int Min(int a, int b) { return (a <= b) ? a : b; }

int Max(int a, int b) { return (a >= b) ? a : b; }

void Hv(int d, int c) {
 int i, istart, q = 0, pmin = c+1, pmax = d-1;
 int vmin, vmax;
   // trang thai q
 vmin = vmax = a[d];
 for (i = d+1; i <= c; ++i) {
      vmin = min(vmin,a[i]);
      vmax = max(vmax,a[i]);
 }
 for (i = vmin; i <= vmax; ++i) {
    switch(q) {
     case 0: // duyet doan toan 0
             if (p[i] > 0) { // Khoi tri khi gap so khac 0
                pmin = pmax = p[i];
                istart = i; q = 1;
             }
             break;
      case 1: // duyet doan khac 0
             if (p[i] > 0) {
                pmin = Min(pmin,p[i]);
pmax = Max(pmax,p[i]);
                             if (pmax-pmin == i-istart && pmax-pmin+1 > dmax){
                                 dmax = pmax-pmin+1;
                                 imax = pmin;
                             }
                      }
                      else q = 0;
                      break;
               } // end switch
             } // end for
        }

        void Ghi() {
          ofstream g(gn);
          g << imax << endl << dmax;
          g.close();
        }

        void Run() {
           int i, v , istart;
           memset(p,0,sizeof(p));
           imax = 0; dmax = 0;
           ifstream f(fn);
           f >> n;
           f >> v; a[1] = v; p[v] = 1; istart = 1;
           for (i = 2; i <= n; ++i) {
               f >> v; a[i] = v;
               if (p[v] >= istart) {
                  if (i-istart > dmax) Hv(istart, i-1);
                  istart = p[v]+1;
               }
               p[v] = i;
           }
           f.close();
           if (n+1-istart > dmax) Hv(istart,n);
           Ghi();
         }


Đoạn nguyên tố
Cho n số tự nhiên ai i = 1..n. Hãy tìm đoạn dài nhất gồm các phần tử là các số nguyên tố.
1  n, ai  100000.
          /* ---------------------------------------
             Primes.cpp
             Tim doan dai nhat gom cac so nguyen to
          ----------------------------------------*/
          #include <fstream>
          #include <iostream>
          #include <math.h>

        using namespace std;

        // D A T A        A N D      V A R I A B L E S

        const int mn = 100001;
        const char * fn = "primes.inp";
char p[mn]; // p[i] = 0=> i nguyen to
      int n;
      int imax, dmax;

      // P   R O T O T Y P E S
      void   Run();
      void   Sang(int);
      void   Duyet();

      // I M P L E M E N T A T I O N
      int main() {
        Run();
        cout << endl << " Fini ";
        cin.get();
         return 0;
      }

      void Sang(int n) {
        int can = (int) sqrt(n);
        int i, j, k;
        p[0] = p[1] = 1;
        for (i = 2; i <= can; ++i)
          if (p[i]==0)
             for (k = n/i, j = i; j <= k; ++j) p[i*j] = 1;
      }

      void Duyet() {
        int i, x, d;
        imax = 0; dmax = 0; d = 0;
        ifstream f(fn);
        f >> n;
        for (i = 1; i <= n; ++i) {
           f >> x; cout << x << " ";
           if (p[x]) { // x khong nguyen to
             if (d > dmax) {
               dmax = d; imax = i;
             }
             d = 0;
           } else ++d;
        }
        f.close();
        if (d > dmax) {
            dmax = d; imax = n+1;
         }
        imax = imax - dmax;
      }

      void Run() {
        Sang(mn);
        Duyet();
        cout << endl << imax << " " << dmax;
      }


Đoạn nguyên tố cùng nhau
Cho n số tự nhiên ai i = 1..n. Hãy tìm đoạn dài nhất gồm các phần tử, trong đó hai phần tử kề nhau thì
nguyên tố cùng nhau.
1  n, ai  100000.


         /* ---------------------------------------
           Primes.cpp
           Tim doan dai nhat gom cac so nguyen to
           cung nhau
         ----------------------------------------*/
         #include <fstream>
         #include <iostream>
         #include <math.h>

         using namespace std;

         // D A T A      A N D     V A R I A B L E S

         const int mn = 100001;
         const char * fn = "coprime.inp";
         char p[mn]; // p[i] = 0=> i nguyen to
         int n;
         int imax, dmax;

         // P R O T O T Y P E S
         void Run();
         void Duyet();
         int Gcd(int, int);

         // I M P L E M E N T A T I O N
         int main() {
           Run();
           cout << endl << " Fini ";
           cin.get();
            return 0;
         }

         int Gcd(int a, int b) {
           int r;
           while (b) {
             r = a % b; a = b; b = r;
           }
           return a;
         }

         void Duyet() {
           int i, x, y, d; // d dem so cap nguyen to cung nhau
           imax = 0; dmax = 0; d = 0;
           ifstream f(fn);
           f >> n >> x;
           for (i = 2; i <= n; ++i) {
             f >> y;
             if (Gcd(x,y) == 1) ++d;
             else {
               if (d > dmax) {
               dmax = d; imax = i;
               }
d = 0;
              }
              x = y;
            }
            f.close();
            if (d > dmax) {
               dmax = d; imax = n;
             }
             ++dmax; imax = imax - dmax;
        }

        void Run() {
          Duyet();
          cout << endl << imax << " " << dmax;
        }


Bài toán Euler N 28
Trong ma trận vuông n X n, n lẻ xuất phát từ ô giữa ghi lần lượt các số từ 1..n2 theo đường xoắn ốc theo
chiều kim dồng hồ. Với n = 5 tổng các phần tử trên 2 đường chéo là 101. Hãy tính tổng này với n = 1001.




                                   21     22     23     24     25

                                   20     7      8      9      10

                                   19     6      1      2      11

                                   18     5      4      3      12

                                   17     16     15     14     13




Nhận xét
Xét các số nằm ở bốn góc của hình vuôn cạnh n. Số ở góc trên phải là n2, các số còn lại
lần lượt là n2−(n−1), n2−2(n−1) và n2−3(n−1). Tổng 4 số này là
d(n) = n2 + n2−(n−1) + n2−2(n−1) + n2−3(n−1) = 4n2 − 6(n−1), n > 1
Với n = 1, d(1) = 1.
Tổng các số trên hai đường chéo chính là
s(n) = d(1) + d(3) +…+d(n).


        /*
            Name: Problem Euler 28
Copyright: NXH 2010
              Author: Nguyen Xuan Huy
              Date: 10/06/10 10:53
              Description:
              2 mau (den, trang) va 16 mau
         */

         #include <iostream>
         #include <stdio.h>

         using namespace std;

         int Euler28(int n) {
            int e = 1;
            for (int i = 3; i <= n; i += 2)
                e += 4*i*i - 6*(i-1);
            return e;
         }

         main() {
           cout << endl << Euler28(1001); // 669171001
           cout << endl << " Fini";
           cin.get();return 0 ;
         }


Bài toán Euler N 52
Số x = 125874, và 2x = 251748 được tạo bởi cùng chữ số theo trật tự khác nhau. Tìm số tự nhiên nhỏ nhất
x sao cho 2x, 3x, 4x, 5x và 6x có các chữ số ddoooi một khác nhau và là những hoán vị của x. (142857)
Có trên 5 số thỏa tính chất trên hay ko?
Nếu ko đòi hỏi các chữ số khác nhau?
Với các số nguyên dương 1..4294967295 (232 − 1) có 10 số
1. : 142857
2. : 1428570
3. : 1429857
4. : 14285700
5. : 14298570
6. : 14299857
7. : 142857000
8. : 142985700
9. : 142998570
10. : 142999857


Chương trình tính và ghi file 10 số


         /*
              Name: Problem Euler 28
              Copyright: NXH 2010
              Author: Nguyen Xuan Huy
Date: 10/06/10 10:53
  Description:
  2 mau (den, trang) va 16 mau
*/

#include <iostream>
#include <fstream>

typedef uint32_t   UI;

const UI maxint = (UI)0xffffffff;

using namespace std;

char digits[10];
char mark[10];

void GetDigits(UI x, char dg[]) {
    for (int i = 0; i < 10; ++i) dg[i] = 0;
    do {
       ++dg[x%10];
       x /= 10;
    } while (x);
}

bool SameDigits(UI x) {
    GetDigits(x,mark);
    for (int i = 0; i < 10; ++i)
      if (digits[i] != mark[i]) return false;
    return true;
}

bool Test(UI x) {
  GetDigits(x,digits);
  UI d = x;
     x += d; // 2x
  for (int i = 2; i <= 6; ++i, x += d) {
     if (!SameDigits(x)) return false;
  }
  return true;
}

UI Euler52(UI n) {
    bool b;
   for (UI x = n+1; x <= maxint;    ++x)
       if (Test(x)) return x;
   return 0;
}

int Euler52m() { // co tren 10 so Euler52?
     int d;
     UI x = 0;
     ofstream f("Euler52.txt");

     for (d = 0; (x = Euler52(x)) > 0;) {
         ++d;
         f << endl << d << ". : " << x;
// if (d == 10) break;
                }
                f.close();
                return d;
         }

         main() {

              int d = Euler52m();
              cout << "nn Found: " << d;
              cout << endl << " Fini";
              cin.get();return 0 ;
         }

Các số nguyên tố


Tìm được 340059 số, số cuối là 4870843 với lặp n < 500.
Ý tưởng: nếu p[1..n] , c = p[n] là danh sách n số nguyên tố đầu tiên và x là một số lẻ trong khoảng c..c*c thì
x là nguyên tố khi và chỉ khi x không có ước nguyên tố trong khoảng 3..     x.
Xuất phát p = (2,3) − 2 số nguyên tố đầu tiên. n = 2 là số lượng các số nguyên tố hiện tìm được.
Find: xét các số lẻ x trong khoảng từ pn + 2 đến pn2 − 2. Chú ý, do pn là số lẻ nên pn2 lẻ. Nếu x là ng tố thì
thêm vào dãy.
Để kiểm tra tính ng tố của x



         /*
             Name: Problem Euler 28
             Copyright: NXH 2010
             Author: Nguyen Xuan Huy
             Date: 10/06/10 10:53
             Description:
             2 mau (den, trang) va 16 mau
         */

         #include <iostream>
         #include <math.h>

         typedef uint32_t           UI;

         const UI maxn = 20000000;

         using namespace std;

         UI p[maxn];
         UI n;

         bool IsPrime(UI x) {
            UI can = (UI) sqrt(x);
            for (UI i = 2; p[i] <= can; ++i)
                if (x % p[i] == 0) return false;
            return true;
         }
// Tim tu n
       void Find() {
          UI n2 = p[n]*p[n] - 2;
          for (UI i = p[n] + 2; i <= n2; i += 2)
              if (IsPrime(i)) p[++n] = i;
       }


       void Run() {
         p[1] = 2; p[2] = 3;
         n = 2;
         while (n < 500) {
                cout << endl << " Find with n = " << n;
                Find();
                cout << endl << " Tong cong " << n << " so nguyen to ";
                cout << endl << " So nguyen to lon nhat: " << p[n];
                cin.get();
         }
       }

       main() {
         Run();
         cout << endl << " Fini";
         cin.get();
       return 0 ;
       }


Problem Euler 1

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and
9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.



       /*
         Name: Problem Euler 01
         Copyright: NXH 2010
         Author: Nguyen Xuan Huy
         Date: 10/06/10 10:53
         Description:
         If we list all the natural numbers below 10 that are multiples
       of 3 or 5,
         we get 3, 5, 6 and 9. The sum of these multiples is 23.
         Find the sum of all the multiples of 3 or 5 below 1000.

       */

       #include <iostream>
       #include <stdio.h>

       const int mn = 1001;
using namespace std;

         inline int Min(int a, int b) { return (a <= b) ? a : b; }

         int Euler01(int maxval) {
              int a[mn];
              int n;
              int i3 = 0, i5 = 0;
              int s = 0;
              n = 0;
              a[0] = 1;
              while (a[n] < maxval) {
                   while (3*a[i3] <= a[n]) ++i3;
                   // 3*a[i3] > a[n]
                   while (5*a[i5] <= a[n]) ++i5;
                   // 5*a[i5] > a[n]
                   a[++n] = Min(3*a[i3],5*a[i5]);
            }
            if (a[n] > maxval) --n;
            for (int i = 1; i <= n; ++i) s += a[i];
            return s;
         }

         main() {

             cout << endl << Euler01(1000); // 3822
             cout << endl << " Fini";
             cin.get();
         return 0 ;
         }




Each new term in the Fibonacci sequence is generated by adding the previous two terms.
By starting with 1 and 2, the first 10 terms will be:

                              1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

Find the sum of all the even-valued terms in the sequence which do not exceed four
million.

Thuật toán

Ta có f1 = 1; f2 = 2 là số chẵn đầu tiên. Vì tổng lẻ + lẻ = chẵn và lẻ + chẵn = chẵn + lẻ =
lẻ, nên sâu 2 số lẻ ta mới gặp 1 số chẵn. Từ đó suy ra các fi với i = 2 + 3k, k = 0, 1, … là
các số chẵn. của số chẵn. Gọi fi là số chẵn, ta có:

fi−1 là số lẻ sát trước fi.

fi+1 = fi−1 + fi (lẻ),
fi+2 = fi + fi+1 = fi + fi−1 + fi = 2fi + fi−1 (lẻ),

fi+3 = f i+1 + fi+2 = fi−1 + fi + 2fi + fi−1 = 3fi + 2fi−1 = 2fi+2 − fi (chẵn)

Ta sử dụng biến odd để lưu số lẻ và biến even lưu số chẵn sát sau odd. Ta có,

Khởi trị: odd = 1; even = 2;

Bước tiếp theo: odd = 2*even + odd; even = 2*odd – even;




         /*
           Name: Problem Euler 02
           Copyright: NXH 2010
           Author: Nguyen Xuan Huy
           Date: 10/06/10 10:53
           Description:
         Each new term in the Fibonacci sequence is generated by adding
         the previous two terms. By starting with 1 and 2, the first 10
         terms will be:

         1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...


         Find the sum of all the even-valued terms in the sequence
         which do not exceed four million.

         */

         #include <iostream>
         #include <stdio.h>

         using namespace std;

         int Euler02(int maxval) {
             int odd = 1, even = 2;
             int s = even;
             while (even < maxval) {
                   odd = 2*even + odd;
                   even = 2*odd - even;
                   s += even;
             }
            if (even > maxval) s -= even;
            return s;
         }

         main() {
           cout << endl << Euler02(4000000); // 4613732
           cout << endl << " Fini";
           cin.get();
         return 0 ;
         }
Euler03

The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?




Khởi trị với danh sách chứa k = 2 số nguyên tố p[1] = 2; p[2] = 3. Hàm NextPrime(k) tìm số nguyên tố sát
sau sô nguyên tố p[k]. Gọi hàm NextPrime nhiều lần đến khi thu được số nguyên tố p[k] >      n . DuThe
prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ? (6857)


        /*
          Name: Euler03
          Copyright: NXH 2010
          Author: Nguyen Xuan Huy
          Date: 10/06/10 10:53
          Description:
        The prime factors of 13195 are 5, 7, 13 and 29.

        What is the largest                        prime        factor       of     the      number
        600851475143 ?
        */

        #include         <iostream>
        #include         <stdio.h>
        #include         <math.h>
        #include         <time.h>

        using namespace std;

        typedef long double BI; //                       kieu gia nguyen

        BI p[100000];
        BI x = 600851475143.0;

        // Test if y is a prime number

        bool IsPrime(BI y) {
          BI ys = floorl(sqrt(y));
          for (int i = 1; p[i] <= ys; ++i)
            if (fmodl(y,p[i]) == 0.0) return false;
          return true;
        }
// Given a list consequenced first k prime numbers
// p[1..k] = (3,5,7,..p[k]), k > 1.
// Find the next prime number (after p[k])

inline BI NextPrime(int k) {
  BI n;
  for (n = p[k] + 2; !IsPrime(n); n += 2);
  return n;
}

// Find the lagest prime factor of a given integer n

BI Euler03(BI n) {
    BI sn;
    int i, imax, k = 2;
    BI m;
  // Initialize: p[1..2] = (2,3); k = 2: two first
prime numbers (2 and 3).
  p[1] = 2; p[2] = 3;
  sn = floorl(sqrt(n));
  cout << endl << " n = " << n << "      sqrt(n) = " <<
sn;
      // Find consequenced prime numbers not greater
than sqrt(n)
      while (p[k] < sn) {
          m = NextPrime(k);
          p[++k] = m;
      }
      cout << endl << " Found " << k << " prime
number(s). " << endl;
      cout << endl << " The last one is : " << p[k];
      // Find the last index imax in p[1..k] so p[imax]
is a prime factor of n
      imax = 0;
      for (i = 1; i <= k; ++i)
          if (fmodl(n,p[i]) == 0.0) {
             imax = i;
             do { n /= p[i]; } while (fmodl(n,p[i]) ==
0.0);
          }
      cout << "n Now n = " << n << "       imax = " <<
imax;
      cout << " pmax = " << p[imax] << endl;

     // if imax = 0: n did not have any prime factors
in p[1..k]
// so n is a prime number. We return n
              if (imax == 0) return n; // n is a prime number

              return (n < p[imax]) ? p[imax] : n;

       }

       main() {
         time_t t1,t2;
         t1 = time(NULL);
         BI r = Euler03(x); // 6857
         t2 = time(NULL);
         float d = difftime(t2,t1);// sec.
         cout << endl << " Time: " << d << " sec. " << endl;
         cout << "nn The result: " << r;
         cout << endl << " Fini";
         cin.get();
         return 0;

       }


Euler04

A palindromic number reads the same both ways. The largest palindrome made from the
product of two 2-digit numbers is 9009 = 91 99.

Find the largest palindrome made from the product of two 3-digit numbers.

        /*
         Name: Problem Euler 04
         Copyright: NXH 2010
         Author: Nguyen Xuan Huy
         Date: 10/06/10 10:53
         Description:
       Euler04
       A palindromic number reads the same both ways. The
       largest palindrome made from the product of two 2-
       digit numbers is 9009 = 91  99.
       Find the largest palindrome made from the product of
       two 3-digit numbers.

       */

       #include <iostream>
       #include <stdio.h>;
using namespace std;

int Exp(int a, int x) {
   int z = 1;
   while (x) {
     if (x % 2 == 1) z *= a;
     a = a * a;
     x /= 2;
   }
   return z;
}

int Rev(int z) {
     int x = 0;
     while(z) {
       x = x * 10 + (z % 10);
       z /= 10;
     }
     return x;
}

void Euler04(int digitNumber, int & x, int & y) {
    int maxNum = Exp(10,digitNumber) - 1;
    int z;
    for (x = maxNum; x >= 0; --x)
       for (y = maxNum; y >= 0; --y){
       z = x*y;
       if (z == Rev(z)) return;
    }
}

main() {
  int x, y;
  Euler04(3,x,y);
  cout << endl << x << " * " << y << " = " << (x * y);
// x = 999, y = 91, x*y = 90909
  cout << endl << " Fini";
  cin.get();
return 0 ;
}


1.   9 * 1 = 9
2.   99 * 91 = 9009
3.   995 * 583 = 580085
4.   9999 * 9901 = 99000099
Euler69

Euler's Totient function, φ(n) [sometimes called the phi function], is used to determine the number of
numbers less than n which are relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than
nine and relatively prime to nine, φ(9)=6.
n Relatively Prime Φ(n) n/φ(n)
2 1                   1    2
3 1,2                 2    1.5
4 1,3                 2    2
5 1,2,3,4             4    1.25
6 1,5                 2    3
7 1,2,3,4,5,6         6    1.1666...
8 1,3,5,7             4    2
9 1,2,4,5,7,8         6    1.5
10 1,3,7,9            4    2.5
It can be seen that n = 6 produces a maximum n/φ(n) for n 10.
Find the value of n   1,000,000 for which n/φ(n) is a maximum.
n = 100000 (một trăm ngàn); max phi(i)/i = 5.21354 đạt tại i = 30030; 1 sec.
n = 1000000 (một triệu) ; max phi(i)/i = 5.53939 đạt tại i = 510510 dưới 1 sec.
Nhận xét 1
Nếu n = p1m1 p2m2 … pkmk thì (n) = p1m1−1 p2m2−1 … pkmk−1.(p1−1)(p2−1)…(pk−1).
Do đó , kí hiệu E(n) = n/(n), ta có
                          E(n) = n/(n) = (p1p2…pk)/((p1−1)(p2−1)…(pk−1))         (*)
Kí hiệu S(n) là tập các ước nguyên tố (khác nhau) của n, thí dụ, n = 60 thf S(n) = { 2, 3, 5 }. Công thức (*)
cho ta biết, nếu hai số tự nhiên n và m có S(n) = S(m) thì E(n) = E(m). Thí dụ, n = 10i và m = 10j sẽ cho
cùng giá trị E vì S(n) = S(m) = { 2, 5 }.
Xét dãy liên tiếp các số nguyên tố pi 2, 3, 5,…
Vì p < q suy ra pq−q < pq − p hay q(p−1) < p(q−1) tức q/(q−1) < p/(p−1). Vậy dãy pi/(pi-1) với i = 1, 2
giảm dần: pi/(pi-1) < pi+1/(pi+1-1).
Vậy tích p1/(p1-1) * p2/(p2-1) * … * pk/(pk-1) đạt max khi tích p1p2…pk là số lớn nhất không vượt quá n.
         /*
            Name: Problem Euler 02
            Copyright: NXH 2010
            Author: Nguyen Xuan Huy
            Date: 10/06/10 10:53
            Description:
                 It can be seen that n=6 produces a maximum n/phi(n) for n <=
         10.
                 Find the value of n <=1,000,000 for which n/phi(n) is a
         maximum.

         */
#include   <iostream>
#include   <stdio.h>
#include   <math.h>
#include   <time.h>

using namespace std;

typedef uint32_t UI;

UI p[10];

inline UI Min(UI a, UI b) { return ( a <= b) ? a : b; }

bool IsPrime(UI y) {
  UI ys = (UI)(sqrt(y));
  for (UI d = 1; p[d] <= ys; ++d)
    if (y % p[d] == 0) return false;
  return true;
}

inline UI NextPrime(int k) {
  for (k = p[k] + 2; !IsPrime(k); k += 2);
  return k;
}

float Euler69(UI n, UI & a) {
  int k = 2;
  UI m;
  UI b;
  p[1] = 2; p[2] = 3;
  a = p[1]*p[2]; b = (p[1]-1)*(p[2]-1);
  while (a < n) {
     m = NextPrime(k);
     p[++k] = m;
     a *= m; b *= (m-1);
  }
  if (a > n) { a /= m; b /= (m-1); --k; }
  cout << endl << " Found " << k << " prime numbers " << endl;
  for (int i = 1; i <= k; ++i) cout << p[i] << " ";
  return (float) a/b;
}

void Test() {
       time_t t1,t2;
       t1 = time(NULL);
       UI n;
       float r = Euler69(1000000, n);
       cout << endl << " result: n/phi(n) = " << r << " n = " <<
n;
       t2 = time(NULL);
       float d = difftime(t2,t1);// sec.
       cout << endl << " Time: " << d << " sec. " << endl;
}

main() {
       Test();
       cout << (UI)(2*3*5*7*11*13*17*19);
cout << endl << " Fini";
                     cin.get();
                     return 0;
         }



Problem 5
30 November 2001



2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any
remainder.

What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?


Problem 6
14 December 2001


The sum of the squares of the first ten natural numbers is,
12+ 22 + ... + 102 = 385
The square of the sum of the first ten natural numbers is,
(1 + 2 + ... + 10) 2 = 552= 3025
Hence the difference between the sum of the squares of the first ten natural numbers and the square of the
sum is 3025 − 385 = 2640.
Find the difference between the sum of the squares of the first one hundred natural numbers and the square
of the sum.
2 phương án.
Phương án 1. Tính theo 2 vòng lặp, O(n2).
Phương án 2. O(n).
Các công thức:

             n
S (i )  i  k
         k  i 1



S(i) = i((i+1) + (i+2) + … + n) _(a1 + a2 + … + am)2 = a12 + a22 + … + an2 + a1a2 + a1a3 + … +a1an
          // Find the sum of all the primes below two million.
          #include <iostream>
          #include <math.h>
          using namespace std;

         const int mn = 200;
         int a[mn];

         int Diff1(int n) {
             int s = 0;
int i, j;
               for (i = 1; i < n; ++i)
                 for(j = i+1; j <= n; ++j) s += i*j;
               return 2*s;
         }

         int Diff2(int n) {
             int s;
             int i;
             a[n] = n;
             for (i = n-1; i > 0; --i) a[i] = i+a[i+1];
             s = a[2];
             for(i = 2; i < n; ++i) s += i*a[i+1];
             return 2*s;
         }

         main() {
             cout << endl << Diff1(100);
             cout << endl << Diff2(100);

               cout << endl << " Fini"; cin.get();
               return 0;
         }

Problem 7
28 December 2001
By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13. What is the
10001th prime number?
Phương án 1. Sàng, sau đó duyệt để lấy số thứ n. Trong khoảng 1..200000 có 17984 số nguyên tố.
Phương án 2. NextPrime sinh số nguyên tố sát sau.


         // Find the 10001-th              prime number.

         #include <iostream>
         #include <math.h>
         using namespace std;

         const int mn1 = 200000;
         const int mn2 = 20000;

         char p[mn1+1];
         int a[mn2];

         void Eratosthenes(int n) {
           int i, j, n2, c = (int)sqrt(n);
           memset(p,0,sizeof(p));
           for (i = 2; i < c; ++i)
             if (p[i] == 0) {
                n2 = n/i;
                for (j = i; j <= n2; ++j) p[i*j] = 1;
             }
         }

         int PrimeN1(int n) { // phuong an 1
            int d = 0;
Eratosthenes(mn1);
             for (int i = 2; i < mn1; ++i)
               if (p[i] == 0) {
                  ++d;
                  if (d == n) return i;
                }
         }

         bool IsPrime(int m) {
           int sq = (int)sqrt(m);
           int i;
           for (i = 1; a[i] <= sq; ++i)
             if (m % a[i] == 0) return false;
           return true;
         }

         // Biet k so nguyen to.
         // Yim so thu k+1
         int NextPrime(int k) {
            int m = a[k];
            do {
                 m += 2;
            } while (!IsPrime(m));
            return m;
         }

         int PrimeN2(int n) {
            a[1] = 2; a[2] = 3; // Khoi tri voi 2 so ng to dau tien
            int k = 2, m;
            for (int i = 1; i <= n; ++i) {
                m = NextPrime(k);
                a[++k] = m;
            }
            return a[n];
         }

         main() {
             cout << endl << PrimeN1(10001); // 104743
             cout << endl << PrimeN2(10001); // 104743

               cout << endl << " Fini"; cin.get();
               return 0;
         }

Problem 8
11 January 2002
Find the greatest product of five consecutive digits in the 1000-digit number.

                  73167176531330624919225119674426574742355349194934
                  96983520312774506326239578318016984801869478851843
                  85861560789112949495459501737958331952853208805511
                  12540698747158523863050715693290963295227443043557
                  66896648950445244523161731856403098711121722383113
                  62229893423380308135336276614282806444486645238749
                  30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
                65727333001053367881220235421809751254540594752243
                52584907711670556013604839586446706324415722155397
                53697817977846174064955149290862569321978468622482
                83972241375657056057490261407972968652414535100474
                82166370484403199890008895243450658541227588666881
                16427171479924442928230863465674813919123162824586
                17866458359124566529476545682848912883142607690042
                24219022671055626321111109370544217506941658960408
                07198403850962455444362981230987879927244284909188
                84580156166097919133875499200524063689912560717606
                05886116467109405077541002256983155200055935729725
                71636269561882670428252483600823257530420752963450

Biến thể 1. Tính tổng max của k phần tử liên tiếp. Cửa sổ trượt.


         // Find the greatest sum of k consecutive digits
         // in the n-digit number.

         #include     <iostream>
         #include     <fstream>
         #include     <stdio.h>
         #include     <math.h>
         #include     <time.h>

         using namespace std;

         const char * fn = "Euler8.txt";
         const int mn = 5000;



         char a[mn];
         int n; // so luong phan tu
         int k; // chieu dai doan

         inline bool IsDigit(char c) { return (c >= '0' && c <= '9'); }

         void ReadData() {
           ifstream f(fn);
           int i = 0;
           char c;
           while (!f.eof()) {
              f >> c;
              if (IsDigit(c)) a[++i] = c-'0';
           }
           f.close();
           for (i = 1; i <= n; ++i) cout << (int)a[i];
         }

         inline int Max(int a, int b) { return (a > b) ? a : b; }

         int Sum() {
              int s = 0, smax;
              int i, j;
             // Sum of the first k elements
for (i = 1; i <= k; ++i) s += a[i];
                smax = s; i = 1;
                for (j = k + 1; j <= n; ++j,++i) {
                   s = s + a[j] - a[i];
                   smax = Max(smax,s);
                }
                return smax;
         }

         main() {
           time_t t1,t2;
           t1 = time(NULL);
           ReadData();
           cout << endl << endl << Sum() << endl;
           t2 = time(NULL);
           float d = difftime(t2,t1);// sec.
           cout << endl << " Time: " << d << " sec. " << endl;
           cout << endl << " Fini";
           cin.get();
           return 0;
         }

Biến thể 2. Tích max của k phần tử liên tiếp. Cửa sổ trượt.


         // Find the greatest product of k consecutive digits
         // in the n-digit number.

         #include     <iostream>
         #include     <fstream>
         #include     <stdio.h>
         #include     <math.h>
         #include     <time.h>

         using namespace std;

         const char * fn = "Euler8.txt";
         const int mn = 5000;

         char a[mn];
         int n; // so luong phan tu
         int k; // chieu dai doan

         inline bool IsDigit(char c) { return (c >= '0' && c <= '9'); }

         void ReadData() {
           ifstream f(fn);
           f >> n >> k; cout << endl << " n = " << n << "       k = " << k <<
         endl;
           int i = 0;
           char c;
           while (!f.eof()) {
               f >> c;
               if (IsDigit(c)) a[++i] = c-'0';
           }
           f.close();
           for (i = 1; i <= n; ++i) cout << (int)a[i];
}

         inline int Max(int a, int b) { return (a > b) ? a : b; }

         int Product() {
              int s , smax = 0 ;
              int i, j, d;
              s = 1;
              for (j = 1; j <= k; ++j) s *= a[j];
              smax = Max(smax,s);
              i = 1;
              for (j = k+1; j <= n; ++j, ++i) {
                if (a[i] > 0) s /= a[i];
                else { // a[i] = 0
                  s = 1;
                  for (d = i+1; d < j; ++d) s *= a[d];
                }
                s *= a[j];
                smax = Max(s,smax);
              }
              return smax;
         }

         main() {
           time_t t1,t2;
           t1 = time(NULL);
           ReadData();
           cout << endl << endl << Product() << endl;
           t2 = time(NULL);
           float d = difftime(t2,t1);// sec.
           cout << endl << " Time: " << d << " sec. " << endl;
           cout << endl << " Fini";
           cin.get();
           return 0;
         }

Cải tiến biến thể 2. loại trừ nhưnTích max gx a[j] = 0.
Ch][ng trinhf tinhs car 2 ph][ng ans vaf xacs ddinhj vị trì jmax (phần tử cuối dãy đạt tích max).




         // Find the greatest sum of k consecutive
         // digits in the n-digit number.

         #include     <iostream>
         #include     <fstream>
         #include     <stdio.h>
         #include     <math.h>
         #include     <time.h>

         using namespace std;

         const char * fn = "Euler8.txt";
         const int mn = 5000;
char a[mn];
int n; // so luong phan tu
int k; // chieu dai doan
int jmax;

inline bool IsDigit(char c) { return (c >= '0' && c <= '9'); }

void ReadData() {
  ifstream f(fn);
  f >> n >> k; cout << endl << " n = " << n << "      k = " << k <<
endl;
  int i = 0;
  char c;
  while (!f.eof()) {
      f >> c;
      if (IsDigit(c)) a[++i] = c-'0';
  }
  f.close();
  for (i = 1; i <= n; ++i) cout << (int)a[i];
}

inline int Max(int a, int b) { return (a > b) ? a : b; }

int Product1() {
     int s , smax = 0 ;
     int i, j, d;
     s = 1;
     for (j = 1; j <= k; ++j) s *= a[j];
     if (smax < s) { smax = s; jmax = k; }
     i = 1;
     for (j = k+1; j <= n; ++j, ++i) {
       if (a[i] > 0) s /= a[i];
       else { // a[i] = 0
         s = 1;
         for (d = i+1; d < j; ++d) s *= a[d];
       }
       s *= a[j];
       if (smax < s) { smax = s; jmax = j; }
     }
     return smax;
}

int Product2() {
     int s , smax = 0 ;
     int i = 0, j, d;
     s = 1; d = 0;
     for (j = 1; j <= n; ++j) {
         if (a[j] == 0) {
           i = j; s = 1; d = 0;
         }
         else { // s[j] > 0
            s *= a[j];
            ++d;
            if (d == k) {
              if (smax < s) { smax = s; jmax = j; }
              ++i; s /= a[i]; --d;
            }
}
                  }
                  return smax;
         }


         main() {
           float d;
           time_t t1,t2;
           ReadData();
           t1 = time(NULL);
           cout << endl << endl << Product1() << " jmax = " << jmax <<
         endl;

             t2 = time(NULL);
             d = difftime(t2,t1);// sec.
             cout << endl << " Time: " << d << " sec. " << endl;

           t1 = time(NULL);
           cout << endl << endl << Product2() <<                                 " jmax = " << jmax <<
         endl;

             t2 = time(NULL);
             d = difftime(t2,t1);// sec.
             cout << endl << " Time: " << d << " sec. " << endl;

             cout << endl << " Fini";
             cin.get();
             return 0;
         }




Problem 9
25 January 2002


A Pythagorean triplet is a set of three positive integes (a, b, c), for which,
a2 + b2 = c2.
For example, 32 + 42 = 52 ( 9 + 16 = 25).
There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product abc.
Nhận xét

1. Trong bộ ba Pithagor không có 2 số bằng nhau. Nếu a = b thì c  2 a là số vô tỷ, không phải số
nguyên. Nếu a = c thì b = 0, nếu b = c thì a = 0. Từ nhận xét này ta có thể quy định 0 < a < b < c.
2. Nếu tổng a+b+c = k thì nhận xét 1 cho ta 1 ≤ a ≤ m, a+1 ≤ b ≤ 2m, c = k−a−b; m = k/3.


         /*−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
          There exists exactly one Pythagorean triplet
          for which a + b + c = 1000.
Find the product abc.
        −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−-*/
        #include <iostream>
        #include <stdio.h>

        using namespace std;

        int Pit() {
              const int k = 1000;
              int a, b, c, c2, a2, ab2, lim, lim2;
              lim = k/3; lim2 = 2*lim;
              for (a = 1; a <= lim; ++a) {
                a2 = a*a;
                for (b = a+1; b <= lim2; ++b) {
                    ab2 = a2 + b*b;
                    c = k - a - b;
                    c2 = c*c;
                    if (c*c == ab2)
                       return (a * b * c);
                }
            }
            return 0;
        }

        main() {
          cout << endl << Pit();
          cout << endl << " Fini";
          cin.get();
          return 0;
        }

Problem 10
08 February 2002
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
Find the sum of all the primes below two million.
         // Find the sum of all the primes below two million.

        #include <iostream>
        #include <math.h>
        using namespace std;

        const int mn = 2000000;
        char a[mn];

        void Eratosthenes(int n) {
          int i, j, n2, c = (int)sqrt(n);
          memset(a,0,sizeof(a));
          for (i = 2; i < c; ++i)
            if (a[i] == 0) {
               n2 = n/i;
               for (j = i; j <= n2; ++j) a[i*j] = 1;
            }
        }

        unsigned int Sum(int n) {
unsigned int s = 0;
              Eratosthenes(n);
              for (int i = 2; i <= n; ++i)
                if (a[i] == 0) s += i;
              return s;
         }

         main() {

               int n = mn-1;
               cout << Sum(n); // 1179908154
               cout << endl << " Fini"; cin.get();
               return 0;
         }



Problem 11
22 February 2002



In the 20 20 grid below, four numbers along a diagonal line have been marked in red.

         08   02   22   97   38   15   00   40   00   75   04   05   07   78   52   12   50   77   91   08
         49   49   99   40   17   81   18   57   60   87   17   40   98   43   69   48   04   56   62   00
         81   49   31   73   55   79   14   29   93   71   40   67   53   88   30   03   49   13   36   65
         52   70   95   23   04   60   11   42   69   24   68   56   01   32   56   71   37   02   36   91
         22   31   16   71   51   67   63   89   41   92   36   54   22   40   40   28   66   33   13   80
         24   47   32   60   99   03   45   02   44   75   33   53   78   36   84   20   35   17   12   50
         32   98   81   28   64   23   67   10   26   38   40   67   59   54   70   66   18   38   64   70
         67   26   20   68   02   62   12   20   95   63   94   39   63   08   40   91   66   49   94   21
         24   55   58   05   66   73   99   26   97   17   78   78   96   83   14   88   34   89   63   72
         21   36   23   09   75   00   76   44   20   45   35   14   00   61   33   97   34   31   33   95
         78   17   53   28   22   75   31   67   15   94   03   80   04   62   16   14   09   53   56   92
         16   39   05   42   96   35   31   47   55   58   88   24   00   17   54   24   36   29   85   57
         86   56   00   48   35   71   89   07   05   44   44   37   44   60   21   58   51   54   17   58
         19   80   81   68   05   94   47   69   28   73   92   13   86   52   17   77   04   89   55   40
         04   52   08   83   97   35   99   16   07   97   57   32   16   26   26   79   33   27   98   66
         88   36   68   87   57   62   20   72   03   46   33   67   46   55   12   32   63   93   53   69
         04   42   16   73   38   25   39   11   24   94   72   18   08   46   29   32   40   62   76   36
         20   69   36   41   72   30   23   88   34   62   99   69   82   67   59   85   74   04   36   16
         20   73   35   29   78   31   90   01   74   31   49   71   48   86   81   16   23   57   05   54
         01   70   54   71   83   51   54   69   16   92   33   48   61   43   52   01   89   19   67   48

The product of these numbers is 26 63 78 14 = 1788696.

What is the greatest product of four adjacent numbers in any direction (up, down, left, right, or diagonally)
in the 20 20 grid?
Problem 14
05 April 2002


The following iterative sequence is defined for the set of positive integers:
n → n/2 (n is even)
n → 3n + 1 (n is odd)
Using the rule above and starting with 13, we generate the following sequence:
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not
been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.
Which starting number, under one million, produces the longest chain?
NOTE: Once the chain starts the terms are allowed to go above one million.
Đề: Collatz Problem (Bài toán 3n+1).
Với mỗi số tự nhiên n ta thực hiện các thao tác sau để sinh ra dãy số:
Nếu n lẻ thì thay n bằng 3n+1; ngược lại, nếu n chẵn thì thay nn bằng n/2.
Xác định số n  1000000 (1 triệu) để dãy số sinh ra là dài nhất.
Đáp số: n = 837799 sinh dãy dài 525 số.


Chú ý rằn có thể lúc đầu giá trị n nhỏ (dưới 1 triệu), nhưng sau một số bước giá trị này có thể tăng lên đến
vài tỷ. Thí dụ,
837799 → 2513398 → 1256699→ 3770098→ 1885049→ 5655148→ 2827574→ 1413787→ 4241362
→ 2120681→ 6362044→ 3181022→ 1590511→ 4771534→ 2385767→ 7157302→ 3578651
→ 10735954→ 5367977→ 16103932→ 8051966→ 4025983→ 12077950→ 6038975→ 18116926
→ 9058463→ 27175390→ 13587695→ 40763086→ 20381543→ 61144630→ 30572315→ 91716946
→ 45858473→ 137575420→ 68787710→ 34393855→ 103181566→ 51590783→ 154772350
→ 77386175→ 232158526→ 116079263→ 348237790→ 174118895→ 522356686→ 261178343
→ 783535030→ 391767515→ 1175302546.
Thuật toán
Ta tạm gọi dãy sinh ra là dãy 3n+1
Nhận xét.
1. Dãy 3n+1 là dãy không lặp, do đó sinh xuôi và sinh ngược đều các dãy không lặp
Phương án 1: vét
Ta duyệt với mỗi n = 2..1000000 xác định chiều dài lớn nhất của dãy thu được theo quy tắc ra trong đề bài.
Để ý rằng khi n lẻ ta tính n’ = 3n+1. Ta thấy n’ chẵn và n’ > n trong khi ta cần dãy giảm đến 1. Vì vậy, khi
n lẻ ta tính luôn 2 bước n’ = 3n+1 và n’’ = n’/2 = 2n/2 + (n+1)/2 = n + (n+1)/2. Với thời gian không quá 1’’
bạn có thể tìm ra đáp số n = 837799 đạt chiều dài max = 525.
Để ý rằng nếu n chẵn thì s(n) = s(n/2) + 1
n lẻ thì s(n) = s(n+(n+1)/2) + 2;
Nên chạy trong Turbo Pascal vì Free Pascal không ổn định.
Thay n/2 bằng n >> 1 sẽ nhanh hơn là dùng phép chia.
        (*------------------------------------------------
            Problem Euler 14 Ver. 1
Find an integer n under one million that generates
the longest chain by the following rule
if n is even then replace n by n/2
else replace n by 3n + 1
--------------------------------------------------*)

const mn = 1000000;

function Try(n: longint): longint;
 var d: longint;
begin
 d := 1;
 while (n > 1) do
     if Odd(n) then
     begin
       d := d+2;
       n := n+((n+1) shr 1);
     end
     else
     begin
      inc(d);
      n := n shr 1;
     end;
 Try := d;
end;


procedure Find(mn: longint);
 var i, d, dmax, imax: longint;
begin
  dmax := 1; imax := 1;
  for i := 2 to mn-1 do
  begin
      d := Try(i);
      if (d > dmax) then
      begin
        dmax := d; imax := i;
      end;
 end;
 writeln(dmax,' ', imax);
end;

BEGIN
  Find(mn);
  readln;
END.




/*------------------------------------------------
   Problem Euler 14 Ver. 1
Find an integer n under one million that generates
the longest chain by the following rule
if n is even then replace n by n/2
else replace n by 3n + 1
--------------------------------------------------*/
#include <iostream>
         #include <fstream>

         #define Odd(n) (((n) & one) == one)

         using namespace std;


         typedef unsigned int UI;

         const UI mn = 1000000;
         const UI one = (UI)1;

         int Try(UI n) {
           int d = 1;
           while (n > 1) {
              if (Odd(n)) { d += 2; n = n+((n+1)>>1); }
              else { d++; n >>= 1; }
           }
           return d;
         }


         void Find(int mn) {
           UI i;
           int d, dmax = 1, imax = 1 ;
           for (i = 2; i < mn; ++i) {
              d = Try(i);
              if (d > dmax) {
                 dmax = d; imax = i;
              }
           }
           cout << endl << imax << " " << dmax;
         }

         main() {
           float d;
           time_t t1,t2;
           t1 = time(NULL);
           Find(mn);
           t2 = time(NULL);
           d = difftime(t2,t1);// sec.
           cout << endl << " Time: " << d << " sec. " << endl;
           cout << endl << " Fini";
           cin.get();
           return 0;
         }

Phương án 2. Dùng quy hoạch động hạn chế.
Kí hiệu s(n) là chiều dài dãy 3n+1. Ta có ngay:
s(n) = s(n/2) + 1, nếu n chẵn
s(n) = s(n+(n+1)/2) + 2, nếu n lẻ
Ta sử dụng mảng a gồm mn = 1 triệu phần tử, a[i] = s(i) và sẽ lặp cho đến khi n < mn để đánh dấu
/*--------------------------------------------------------
      Problem Euler 14 Ver. 2
      Find an integer n under one million that generates
      the longest chain by the following rule
      if n is even then replace n by n/2
      else replace n by 3n + 1
  --------------------------------------------------------*/
#include <iostream>
#include <fstream>

#define Odd(n) (n & 1) == 1
#define Even(n) !Odd(n)

using namespace std;


typedef unsigned int UI;

const UI mn = 1000000;

UI a[mn];

int Try(UI n) {
  int d = 1, v = n;
  if (Even(v)) {
    a[n] = a[v/2] + 1; return a[n];
  }
  while (v > 1) {
      if (Odd(v)) { d += 2; v += ((v+1)>>1); }
      else { d++; v >>= 1; }
      if (v < n) {
        if (a[v] > 0) {
         a[n] = a[v] + d - 1;
           return a[n];
      }
    }
  }
  a[n] = d;
  return a[n];
}

int Test(UI n) {
      int d = 1;
  while (n > 1) {
     if (Odd(n)) { d += 2; n += ((n+1)>>1); }
     else { d++; n >>= 1; }
  }
  return d;
}

void Find(int mn) {
  UI i;
  int d, dmax = 1, imax = 1;
  memset(a,0,sizeof(a));
  a[1] = 1;
for (i = 2; i < mn; ++i) {
               d = Try(i);
               if (d > dmax) {
                 dmax = d; imax = i;
               }
            }
            cout << endl << imax << " " << dmax;

        }

        main() {
          float d;
          time_t t1,t2;
          t1 = time(NULL);
          Find(mn);
          t2 = time(NULL);
          d = difftime(t2,t1);// sec.
          cout << endl << " Time: " << d << " sec. " << endl;
          cout << endl << " Fini";
          cin.get();
          return 0;
        }

Phương án 3.
FromOne: Sinh dãy ngược từ 1. Đánh dấu độ dài của các phần tử được sinh mới trong dãy.
Sau đó duyệt lại mảng, xét những phần tử chưa đánh dấu ta sinh xuôi dãy và cũng đánh dấu các phần tử
được sinh trong dãy.

        /*------------------------------------------------
           Problem Euler 14 Ver. 3
        Find an integer n under one million that generates
        the longest chain by the following rule
        if n is even then replace n by n/2
        else replace n by 3n + 1
        --------------------------------------------------*/
        #include <iostream>
        #include <fstream>

        #define Odd(n) (n & 1) == 1
        #define Even(n) !Odd(n)

        using namespace std;

        typedef unsigned int UI;

        const UI mn = 1000000;
        const UI mn2 = mn+2;

        UI     len[mn];
        UI     q[mn];
        UI     v,r; // con tro ngan xep st
        UI     lenmax, imax;

        void FromOne() {
           UI x, y;
           UI s;
memset(len,sizeof(len),0);
      v = r = 0; len[1] = 1;
      q[++v] = 1;
      do {
        x = q[++r]; s = len[x];
        y = 2*x;
        if (y < mn)
          if (!len[y])
          { len[y] = s+1; q[++v] = y; }
        if (Even(x)&&((x-1)%3==0)) {
          y = (x-1)/3;
          if (!len[y])
           { len[y] = s+1; q[++v] = y; }
        }
      } while (v != r);
      lenmax = s ; imax = x;
}

void ToOne(UI n) {
  int d = 1;
  int x = n;
  int s, i;
  q[d] = n;
  if (Even(x)) {
    len[n] = len[x/2] + 1;
    if (lenmax < len[n]) {
        lenmax = len[n];
        imax = n;
    }
    return;
  }
  while (x > 1) {
      if (Odd(x)) { q[++d] = 3*x+1; x = q[d]/2; q[++d] = x; }
      else { x >>= 1; q[d++] = x; }
      if (x < mn)
         if (len[x]) {
          len[n] = len[x] + d - 1;
          if (lenmax < len[n]) {
            lenmax = len[n];
            imax = n;
           }
          s = len[n]+1;
          for (i = 2; i <= d; ++i)
           if (q[i] < mn) len[q[i]] = s-i;
          return;
      }
  }
}

void Find() {
  UI i;
  FromOne();
  for (i = 2; i < mn; ++i)
    if (len[i] == 0) ToOne(i);
  cout << endl << "     lenmax = " << lenmax << "       imax = " <<
imax;
}
main() {
            float d;
            time_t t1,t2;
            t1 = time(NULL);
            Find();
            t2 = time(NULL);
            d = difftime(t2,t1);// sec.
            cout << endl << " Time: " << d << " sec. " << endl;
            cout << endl << " Fini";
            cin.get();
            return 0;
          }

Problem 15
19 April 2002
Starting in the top left corner of a 2 2 grid, there are 6 routes (without backtracking) to the bottom right
corner.




How many routes are there through a 20 20 grid?
Thuật toán
Ta giải bài toán tổng quát với lưới đơn vị gồm chiều dài n, rộng m. Trên lưới vuông này ta đánh nhãn các
cạnh đơn vị nằm ngang là D, dọc là R. Mọi đường đi không quay lui từ góc trên trái đến góc dưới phải đều
phải qua đúng n cạnh ngang và m cạnh dọc. Bài toán quy về dạng tổ hợp như sau. Có thể xây dựng bao
nhiêu xâu kí tự độ dài n+m từ hai kí tự D và R trong đó có đúng n kí tự D và m kí tự R. Như vậy ta chỉ cần
chọn đúng n vị trí trong số các vị trí từ 1..(n+m) để điền kí tự D. Các vị trí còn lại ta điền kí tự R.Vậy đáp
số sẽ là tổ hợp chặp 2 của 2n
                     Cnn+m = (n+m)!/(n!m!) = ((n+1)(n+2)...(n+m))/m!                 (*)
Với n = m = 20 ta phải tính toán với các số lớn của tử và mẫu số trong công thức (*). Ta chọn phương pháp
tính khác, cụ thể là ta dùng tam giác Pascal. Tam giác Pascal có những tính chất sau :
          (T1) Dòng thứ n có n+1 phần tử mã số từ 0 đến n.
          (T2) Phần tử thứ k trên dòng n, Ckn = tổ hợp chặp k của n phần tử = số hạng thứ k trong dạng khai
          triển của nhị thức (a+b)n = tổng của số hạng thứ k và k-1 trên dòng sát trước, dòng n-1 (Ckn= Ckn-1
          + Ck-1n-1).
          (T3) Mỗi dòng là một dãy số được bố trí đối xứng nhau. Tính chất này cho phép ta chỉ cẫn xác
          định các giá trị trên một nửa dòng.
2   11
3   121
4   1331
5   14641
6   1 5 10 10 5 1
7   1 6 15 20 15 6 1



         // Problem Euler 15
         #include <iostream>
         #define Even(c) ((c)&1)==0
         #define Odd(c) !Even(c)
         const int maxN = 21;
         int a[maxN];
         using namespace std;
         int Pas(int n, int m) {
           int nm = n+m, i, i2, j;
           a[0] = a[1] = 1;
           for (i = 2; i <= nm; ++i) {
               i2 = i/2;
               a[i2] = (Odd(i)) ? a[i2]+a[i2-1] : 2*a[i2-1];
               for (j = i2-1; j > 0; --j)
                  a[j] = a[j]+a[j-1];
           }
           return (n < m) ? a[n] : a[m];
         }
         main() {
           cout << endl << Pas(2,2); // 407575348
           cout << endl << " Fini";
           cin.get();
           return 0;
         }

Problem 16
03 May 2002
215 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.
What is the sum of the digits of the number 21000?
Thuật toán Tính trực tiếp.
Khởi trị a = 2;
lặp n lần a = a * 2;
          // Problem Euler 16
          // Tính tong cac chu so cua so 2^1000

         #include <iostream>

         const int maxN = 500;
         int a[maxN];
         int len;

         using namespace std;

         // 2^1
         void Init() {
           memset(a,0,sizeof(a));
           a[0] = 2;
           len = 1;
         }
// a = 2*a
         void Mult2() {
            int c = 0;
            for (int i = 0; i < len; ++i) {
               c = (a[i]<<1) + c;
               a[i] = c % 10;
               c /= 10;
            }
            if (c) a[len++] = c;
         }

         int Sum(int n) {
            int i,s = 0;
            Init();
            for (i = 2; i <= n; ++i)
               Mult2();
            for (i = 0; i < len ; ++i) s += a[i];
           return s;
         }


         main() {
            cout << endl << Sum(1000) << " len = " << len; // 1366, 302 chu
         so
            cout << endl << " Fini";
            cin.get();
            return 0;
         }

Viết dãy số
Nếu sử dụng các tấm bìa, trên mỗi tấm ghi 1 chữ số thì đê ghi liên tiếp các số từ 1 đến 10 ta cần 21 tấm.

              1   2   3   4    5   6   7   8   9   1   0   1   1   1   2   1   3   1   4   1   5


Cần bao nhiêu bìa để viết các số từ 1 đến 1000?



Problem 18
31 May 2002



By starting at the top of the triangle below and moving to adjacent numbers on the row below, the
maximum total from top to bottom is 23.

                                                      3
                                                     7 4
                                                    2 4 6
                                                   8 5 9 3

That is, 3 + 7 + 4 + 9 = 23.
Find the maximum total from top to bottom of the triangle below:

                                         75
                                       95 64
                                      17 47 82
                                    18 35 87 10
                                   20 04 82 47 65
                                 19 01 23 75 03 34
                                88 02 77 73 07 63 67
                              99 65 04 28 06 16 70 92
                             41 41 26 56 83 40 80 70 33
                           41 48 72 33 47 32 37 16 94 29
                          53 71 44 65 25 43 91 52 97 51 14
                        70 11 33 28 77 73 17 78 39 68 17 57
                       91 71 52 38 17 14 91 43 58 50 27 29 48
                     63 66 04 68 89 53 67 30 73 16 69 87 40 31
                    04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

NOTE: As there are only 16384 routes, it is possible to solve this problem by trying
every route. However, Problem 67, is the same challenge with a triangle containing one-
hundred rows; it cannot be solved by brute force, and requires a clever method!)

Thuật toán

Đọc dần dữ liệu tính trọng số tại mỗi đỉnh k. Đỉnh trên cạnh (nằm ngoài cùng ở 2 đầu) chỉ
có 1 cha. Đỉnh trong có 2 cha.

Nếu k là đỉnh đầu tiên của dòng i thì cha của k là k-i+1. Nếu k là đỉnh cuối cùng của dòng
i thì cha của k là k-i. Nếu đỉnh k nằm giữa dòng i thì k có 2 cha là k-i và k-i+1.

        // Problem Euler 16
        // Tính tong cac chu so cua so 2^1000

        #include <iostream>
        #include <fstream>

        #define Max(a,b) ((a > b) ? a : b)

        const int maxN = 5000;
        const char * fn = "E18.inp";
        int a[maxN];

        using namespace std;

        int MaxTotal(){
          int n, i, j, x, k = 1, maxval = 0;
          ifstream f(fn);
          f >> n;
          f >> x; a[k] = x;
          for (i = 2; i <= n; ++i) {
              f >> x; ++k; a[k] = a[k-i+1]+x;
              for (j = 2; j < i; ++j) {
                  f >> x; ++k; a[k] = Max(a[k-i],a[k-i+1]) + x;
              }
f >> x; ++k; a[k] = a[k-i] + x;
             }
             f.close();
             for (j = 0; j < n; ++j)
               maxval = Max(maxval,a[k-j]);
             return maxval;
         }

         main() {
           cout << endl << MaxTotal(); // 1074
           cout << endl << " Fini";
           cin.get();
           return 0;
         }



Problem 19
14 June 2002


You are given the following information, but you may prefer to do some research for yourself.
1 Jan 1900 was a Monday.
Thirty days has September,
April, June and November.
All the rest have thirty-one, Saving February alone,
Which has twenty-eight, rain or shine. And on leap years, twenty-nine.
A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec
2000)?
Problem 20
21 June 2002
n! means n (n 1) ... 3 2 1. Find the sum of the digits in the number 100!
n                                    n!                                    sum
1                                    1                                     1
2                                    2                                     2
3                                    6                                     6
4                                    24                                    6
5                                    120                                   3
6                                    720                                   9
7                                    5040                                  9


Thuật toán
Làm trực tiếp. Biểu diễn số lớn.
        // E20 Tong các chu so cua n!

         #include <iostream>
using namespace std;

        const int mn = 200;
        int a[mn];
        int len;

        void Print(char *msg) {
          cout << msg;
          for (int i = len-1; i >= 0; --i) cout << a[i];
          cout << "(" << len <<")";
        }

        void Init() {
          len = 1;
          a[0] = 1;
        }

        // a = a * x
        void Mult(int x) {
          int c = 0, i;
          for (i = 0; i < len; ++i) {
            c = a[i]*x + c;
            a[i] = c % 10;
            c /= 10;
          }
          while (c) {
             a[len++] = c % 10;
             c /= 10;
          }
        }

        int Sum(int n) {
            int i;
           Init();
           for (i = 2; i <= n; ++i)
               Mult(i);
           int s = 0;
           for (i = 0; i < len; ++i) s += a[i];
           return s;
        }

         main() {
            cout << Sum(100); // 648
            cout << endl << " Fini !";
            cin.get();
         }
Problem 21. Số thân thiện
05 July 2002
Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a b, then a and b are an amicable pair and each of a and b are called
amicable numbers.
For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) =
284. The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.
Evaluate the sum of all the amicable numbers under 10000.
Thuật toán
Nhận xét
5 cặp số thân thiện trong khoảng 1..10000
220 - 284
1184 - 1210
2620 - 2924
5020 - 5564
6232 - 6368
Tổng: 31626.


Dùng mảng a đánh dấu các phần tử đã duyệt, giảm số lần xử lý.
       // E20 Amicable Numbers So than thien
       #include <iostream>
       #include <math.h>
       using namespace std;
       const int mn = 10000;
       int a[mn+1];
       // Tong cac uoc < n cua n
       int Su(int n) {
         int u, c = (int)sqrt(n), s;
         if (n == 1) return 0;
         s = 1;
         if (c*c == n) { s += c; --c; }
         for (u = 2; u <= c; ++u)
              if (n % u == 0) s += u + (n/u);
         return s;
       }
       int Ami(int n) {
         int i, d, j, s = 0, v = 0;
         memset(a,0, sizeof(a));
         for (i = 2; i <= n ; ++i)
              if (a[i] == 0) {
                 ++v;
                 a[i] = 1; d = Su(i);
                 if (d <= n)
                     if (a[d] == 0) {
                        a[d] = 1; j = Su(d);
                        if (j == i && i != d) {
                           cout << endl << i << " " << d;
                           s += i + d;
                        }
                  }
               }
         cout << endl << " So lan duyet: " << v << endl;
         return s;
       }
       main() {
         cout << endl << Ami(mn); // 31626
         cout << endl << " Fini !";
         cin.get();
       }
Problem 22
19 July 2002


Using names.txt (right click and 'Save Link/Target As...'), a 46K text file containing over five-thousand
first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each
name, multiply this value by its alphabetical position in the list to obtain a name score.
For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 =
53, is the 938th name in the list. So, COLIN would obtain a score of 938 53 = 49714.
What is the total of all the name scores in the file?
Problem 23. Số hoàn thiện
02 August 2002


A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For
example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a
perfect number.
A number n is called deficient (hụt, thiếu) if the sum of its proper divisors is less than n and it is called
abundant (trội, dôi, dồi dào) if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as
the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater
than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be
reduced any further by analysis even though it is known that the greatest number that cannot be expressed
as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
Thuật toán
Chương trình khảo sát các loại số.
Scan: Duyệt các số từ 1 đến 28123. Gọi Su(i) là tổng các ước thực sự của số nguyên dương i. Đánh dấu mỗi
số i theo các nhãn:
a[i] = 0: nếu i là số hoàn thiện, tức là Su(i) = i;
a[i] = 1: nếu i là số hụt, tức là Su(i) < i;
a[i] = 2: nếu i là số dôi, tức là Su(i) > i; Đồng thời nhặt riêng các số dôi đưa vào mảng b. Gọi k là biến đếm
các số dôi, ta có mảng b[1..k] chứa toàn số dôi.
Asum: Duyệt các cặp số dôi b[i], b[j], 1  i, j  k. Đánh dấu các số là tổng của b[i] và b[j].
       /*-------------------------------------------
         E23 perfect Numbers So hoan thien
         Su(n) = tong cac uoc that su cua n
         - Su(n) = n: So hoan thien (perfect number)
         - Su(n) < n: So hut (deficient)
         - Su(n) > n: So doi (abundant)
         So tong doi la so la tong cua 2 so doi.
         Moi so tu nhien lon hon 28123 deu la so tong doi.
         Tinh tong cua cac so khong tong doi.
         6965 so abudant.
         Tim duoc 1456 so
       --------------------------------------------*/
       #include <iostream>
       #include <math.h>
       using namespace std;
       #define abundant 2
       #define perfect 0
#define deficient 1
#define asum 4
const int mn = 28124;
int a[mn];
int b[mn]; // danh sach cac so abudant
int k;

// Tong cac uoc < n cua n
int Su(int n) {
  int u, c = (int)sqrt(n), s;
  if (n == 1) return 0;
  s = 1;
  if (c*c == n) { s += c; --c; }
  for (u = 2; u <= c; ++u)
      if (n % u == 0) s += u + (n/u);
  return s;
}
int Scan() {
  int i, d, k = 0;
  memset(a,0,sizeof(a));
  for (i = 1; i < mn; ++i) {
    d = Su(i);
    if (d == i) a[i] = perfect;
       else if (d < i) a[i] = deficient;
         else {
              a[i] = abundant;
              b[++k] = i;
         }
  }
  return k;
}
void ShowAbudant() {
      int i;
  cout << endl << k << " so abudant: " << endl;
  cout << endl; cin.get();
  for (i = 1; i <= k; ++i) cout << b[i] << " ";
}
void ShowAll() {
      int i;
  cout << endl << " Danh sach tong the: " << endl;
  for (i = 1; i < mn; ++i) {
      cout << i << ":";
      switch(a[i]) {
        case perfect: cout << "p "; break;
        case deficient: cout << "d "; break;
        case abundant: cout << "a "; break;
      }
  }
}
// Cac so la tong cua 2 abudant
int Asum() {
  int i, j, s;
  for (i = 1; i <= k; ++i)
    for (j = 1; j <= k; ++j) {
         s = b[i]+b[j];
         if (s < mn) a[s] = asum;
    }
s = 0;
                 for (i = 1; i < mn; ++i)
                   if (a[i] != asum) {
                      s += i;
                   }
                 return s;
          }

          void TestAbudant() {
            int i;
            for (i = 1; i <= k; ++i) {
               cout << endl << b[i] << " " << Su(b[i]); cin.get();
             }
          }

          void Run() {
            int i;
            k = Scan();
            cout << endl << endl << " Total: " << Asum(); // 4179871

          }

        main() {
           Run();
           cout << endl << " Fini !";
           cin.get();
        }
Problem 25
30 August 2002
The Fibonacci sequence is defined by the recurrence relation:
Fn = Fn   1   + Fn 2, where F1 = 1 and F2 = 1.
Hence the first 12 terms will be:
F1=1, F2=1, F3=2, F4=3, F5=5, F6=8, F7=13, F8=21, F9=34, F10=55, F11=89, F12 = 144.
The 12th term, F12, is the first term to contain three digits.
What is the first term in the Fibonacci sequence to contain 1000 digits?
Tính trực tiếp
Cài đặt phép cộng và phép gán các số lớn.
Tính gần đúng:

                 1 5
Kí hiệu             . Gọi fn là số Fibonacci thứ n (tính từ 1). Dùng qui nạp toán học có thể chưng minh
                   2
được n-2 < fn  n-1. Ta biết số chữ số của số nguyên dương n là int(lg(n))+1, trong đó int(x) là hàm lấy
phần nguyên của số thực x, lg là hàm logarithm cơ số 10 (trong C viết là log10).
Vì hàm lg là đồng biến nên bất phương trình kép trên cho ta
(n-2) + 1 < lg(fn) + 1  (n-1) +1.


          /*-------------------------------------------
            E25 Find the first term of Fibonacci sequence
            to contain 1000 digits
          --------------------------------------------*/
          #include <iostream>
#include <math.h>


using namespace std;

#define Max(a,b) ((a > b) ? a : b)
#define Min(a,b) ((a > b) ? b : a)

const int mn = 1000;
int lena, lenb, lenc;
int a[mn], b[mn], c[mn];

// c = a + b;
void Plus() {
   memset(c,0,sizeof(c));
   lenc = Max(lena, lenb);
   int i, v = 0;
   for (i = 0; i < lenc; ++i) {
       v = v + a[i] + b[i];
       c[i] = v % 10;
       v /= 10;
   }
   while (v) { c[lenc++] = v % 10; v /= 10;}
}



int Fib(int n) {
  memset(a,0,sizeof(a));
  memset(b,0,sizeof(b));
  memset(c,0,sizeof(c));
  a[0] = b[0] = 1;
  lena = lenb = 1;
  lenc = 0;
  int i = 2;
  while (lenc < n) {
     Plus();
     memcpy(a, b, sizeof(b)); lena = lenb;
     memcpy(b, c, sizeof(c)); lenb = lenc;
     ++i;
  }
  return i;
}

// f(n) <= x^(n-1); m = lg(f{n)) = (n-1)lg(x)
// m/lg(x)+1
main() {
         cout << endl << endl ;
  int n = 1000;    int m;
  float x = (sqrt(5)+1)/2;
  float y = log10(x);
  cout << endl << (int)pow(x,n-2) << " " << (int)pow(x,n-1);

  cout   <<   endl   <<   (int(n/y) + 1);
  cout   <<   endl   <<   Fib(n); // 4785
  cout   <<   endl   <<   lena << " " << lenb << " " << lenc;
  cout   <<   endl   <<   " Fini !";
cin.get();
        }
Problem 26
13 September 2002


A unit fraction contains 1 in the numerator. The decimal representation of the unit fractions with
denominators 2 to 10 are given:

       1           = 0.5
           /2

       1           = 0.(3)
           /3

       1           = 0.25
           /4

       1           = 0.2
           /5

       1           = 0.1(6)
           /6

       1           = 0.(142857)
           /7

       1           = 0.125
           /8

       1           = 0.(1)
           /9

       1           = 0.1
           /10
Where 0.1(6) means 0.166666..., and has a 1-digit recurring cycle. It can be seen that 1/7 has a 6-digit
recurring cycle.
Find the value of d 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part.


Fib
Dãy số nguyên đựơc gọi là tựa Fibonacci ...,f-2, f-1, f0, f1, f2, ... có tính chất fi = fi-1+fi-2. Biết 5 giá trị
i, fi, j, fj, n, i ≠ j. Viết hàm Fib(i, fi, j, fj, n) tính fn ?
Thí dụ Fib(0, 7, -5, -1, 3) = 29. Ý nghĩa: Biết f0 = 7, f-5 = -1 ta tính được f3 = 29
  -1 2 1 3 4 7 11 18 29
Thuật toán
Giả sử i < j.
Nếu n = i ta cho kết quả fn = fi,
Nếu n = j ta cho kết quả fn = fj,
Nếu n ≠ i ta xử lý như sau.
Gọi ci, i = 1, 2, … là dãy Fibonacci chuẩn, tức là dãy c1=1, c2=1, c3 = c2+c1 = 2, ,..., ci = ci-1+ci-2 ; i = 3, 4,
…. Ta tìm cách biểu thị dãy tựa Fibonacci qua dãy Fibonacci chuẩn.
Đặt k = j-i. Đặt fi = a, fi+1 = b. Ta có
fi = a,
fi+1 = b,
fi+2 = fi+1 + fi = a + b,
fi+3 = fi+2 + fi+1 = (a+b) + b = a + 2b = a.c2 + b.c3
fi+4 = fi+3 + fi+2 = (a.c2 + b.c3) + (a+b) = a(c2 + 1) + b(c3+1) = a.c3 + b.c4.
Tổng quát:
                                 fj = fi+k = a.ck-1 + b.ck = fi.ck-1 + b.ck       (*)
Từ đây suy ra b = (fj - fi.ck-1)/ck
Biết fi = a, fi+1 = b ta dễ dàng tính được n như sau:
Nếu i < n ta tính dần fi theo chiều xuôi, từ i = i+2 đến n:
              if (i < n) {
                   for (i = i+2; i <= n; ++i) {
                        t = a + b; a = b; b = t;
                   }
                   return b;
              }
            Nếu i > n ta tính theo chiều ngược ta tính dần fi theo chiều ngược, từ i = i-1 lui về n:
            // i > n
              for (i = i-1; i >= n; --i) {
                        t = b - a; b = a; a = t;
              }
              return a;

Chương trình


            /*------------------------------------------------
                Fibonacci
            --------------------------------------------------*/
            #include <iostream>

            using namespace std;

            int Fb(int k, int fi, int fj) {
              int t = 1, s = 1;
              int i, c;
              for (i = 3; i <= k; ++i) {
                  c = t + s;
                  t = s; s = c;
              }
              return (fj-fi*t)/s;
            }

            int Fib(int i, int fi, int j, int fj, int n) {
               if (n == i) return fi;
               if (n == j) return fj;
               int t, k, a, b;
               if (i > j) {
                 t = i; i = j; j = t;
                 t = fi; fi = fj; fj = t;
               }
k = j-i;
     a = fi;
     b = Fb(k,fi, fj); // a = f(i) , b = f(i+1)
     if (i < n) {
        for (i = i+2; i <= n; ++i) {
           t = a + b; a = b; b = t;
        }
        return b;
     }
     // i > n
     for (i = i-1; i >= n; --i) {
           t = b - a; b = a; a = t;
     }
     return a;
}
main() {
  cout << endl << Fib(5, 76, 0, 7, 3);
  cout << endl << " Fini";
  cin.get();
  return 0;
}



Sudoku

/*
  Name: Sudoku
  Copyright:
  Author: Nguyen Xuan Huy
  Date: 25/08/10 14:50
  Description: Search Binary Trees

*/

#include <iostream>
#include <fstream>
#include <math.h>

const char BL = 32;
const int mn = 10;
typedef int mi1[mn];
typedef mi1 mi2[mn];

mi2 s,a;
mi1 c;

using namespace std;

// Hien thi
void Print(mi2 s) {
  cout << endl;
  for (int i = 0 ; i < 9; ++i) {
      cout << endl;
      for (int j = 0; j < 9; ++j)
          cout << s[i][j] << BL;
  }
}
// cac mau da dung tren dong i
void Row(int i) {
  for (int j = 0; j < 9; ++j) ++c[s[i][j]];
}
// cac mau da dung tren dong j
void Colum(int j) {
  for (int i = 0; i < 9; ++i) ++c[s[i][j]];
}
// cac mau da dung trong khoi (x,y)
void Square(int x, int y) {
   for (int i = 3*x; i < 3*x+3; ++i)
     for (int j = 3*y; j < 3*y+3; ++j)
       ++c[s[i][j]];
}

bool TestRow() {
  for (int i = 0; i < 9; ++i) {
     memset(c,0,sizeof(c));
     for (int j = 0; j < 9; ++j)
       if (c[s[i][j]]) return false;
       else ++c[s[i][j]];
  }
  cout << endl << "Rows OK";
  return true;
}

bool TestColum() {
  for (int j = 0; j < 9; ++j) {
     memset(c,0,sizeof(c));
     for (int i = 0; i < 9; ++i)
       if (c[s[i][j]]) return false;
       else ++c[s[i][j]];
  }
  cout << endl << "Colums OK";
  return true;
}

bool TestSquare() {
  int i, j, x, y;
  for (x = 0; x < 3; ++x)
    for (y = 0; y < 3; ++y) {
      memset(c,0,sizeof(c));
      for (i = 3*x; i < 3*x+3; ++i)
        for (j = 3*y; j < 3*y+3; ++j)
          if (c[s[i][j]]) return false;
          else ++c[s[i][j]];
  }
  cout << endl << "Squares OK";
  return true;
}

bool Test() {
   return TestRow() && TestColum() && TestSquare();
}

void Print(mi1 a, int d, int c) {
for (int i = d; i <= c; ++i) cout << a[i] << BL;
}
// xac dinh mau cho o (i,j)
int FindColour(int i, int j) {
    memset(c,0,sizeof(c));
    Row(i); Colum(j); Square(i/3,j/3);
    // Print(c,1,9); cin.get();
    for (int k = s[i][j]+1; k <= 9; ++k)
      if (c[k] == 0) return k;
    return 0;
}
/*---------------------------
  Sinh nhanh 1 cau hinh chuan
+-----------------------+
| 1 2 3 | 4 5 6 | 7 8 9 |
| 4 5 6 | 7 8 9 | 1 2 3 |
| 7 8 9 | 1 2 3 | 4 5 6 |
|-------+-------+-------|
| 2 3 4 | 5 6 7 | 8 9 1 |
| 5 6 7 | 8 9 1 | 2 3 4 |
| 8 9 1 | 2 3 4 | 5 6 7 |
|-------+-------+-------|
| 3 4 5 | 6 7 8 | 9 1 2 |
| 6 7 8 | 9 1 2 | 3 4 5 |
| 9 1 2 | 3 4 5 | 6 7 8 |
+-----------------------+
*/
void Gen(int n) {
    int n2 = n*n;
    for (int i = 0; i < n2; i++)
           for (int j = 0; j < n2; j++)
                  s[i][j] = (i*n + i/n + j) % n2 + 1;

}
/*------------------------------
  s: ma tran Sudoku
  a: ma tran cho truoc
  cac gia tri a[i] > 0 can bao luu
  --------------------------------*/
void Read(){
   ifstream f("sudoku.inp");
   for (int i = 0; i < 9; ++i)
     for (int j = 0; j < 9; ++j) {
       f >> s[i][j]; a[i][j] = s[i][j];
   }
   f.close();
}
// Chuyen tu o (i,j) sang o ke tiep
void Next(int &i, int &j) {
   if (j < 8) ++j;
   else { ++i; j = 0; }
}
// chuyen tu o (i,j) sang o ke truoc
void Pred(int &i, int &j) {
   if (j > 0) --j;
   else { --i; j = 8; }
}
void Run() { // back tracking
  Read(); // Print(s); Print(a);
  int i = 0, j = 0, cc;
  while(true) {
     if (i < 0) {
       cout << endl << " Vo nghiem"; return;
     }
     if (i > 8) {
         Print(s);
         if (Test()) cout << endl << "Ok";
         else cout << endl << "ERROR !!!";
         return;
     }
     if (a[i][j]) Next(i,j); // mau co dinh
     else { // can tim mau cho o [i][j]?
       cc = FindColour(i,j);
       if (cc > 0) { // tim duoc
          s[i][j] = cc; Next(i,j);
       }
       else { // ko tim duoc
          s[i][j] = 0;
          Pred(i,j); // lui 1 o
          while (i >= 0 && a[i][j]) Pred(i,j);
       }
     }
   }
}
main() {
  Run();
   cout << endl << " Fini "; cin.get();
  return 0;
}

More Related Content

DOCX
Bai de quy
PDF
Tut4 solution
PDF
Stl string
DOCX
Data Structures and Algorithms Gụidance
PDF
Tut6 solution
PDF
Ktlt lab full
PDF
Tut5 solution
DOC
30 bai he co dap an luyen thi dai hoc
Bai de quy
Tut4 solution
Stl string
Data Structures and Algorithms Gụidance
Tut6 solution
Ktlt lab full
Tut5 solution
30 bai he co dap an luyen thi dai hoc

What's hot (15)

DOCX
Hướng dẫn làm bt về chuỗi.doc
DOCX
Code thực hành tin học đại cương. ctgttp
PDF
Local sakainame 501127 ktl_trình hlmt1 a01 fall 2013 _ modules
PPT
ôN thi hk 1 đề 10
PDF
C8 object-oriented thinking
PDF
PPT
Lập trình hướng đối tượng - p3
PDF
Giao trinh phuong phap tinh 1
PDF
10.khaosaths
DOC
OT HK II - 11
PDF
Www.mathvn.com 200 cau-khaosathamso2
PDF
Nmlt c13 con_tronangcao_in
PDF
Bdt dua ve mot bien
PPT
Lesson08
Hướng dẫn làm bt về chuỗi.doc
Code thực hành tin học đại cương. ctgttp
Local sakainame 501127 ktl_trình hlmt1 a01 fall 2013 _ modules
ôN thi hk 1 đề 10
C8 object-oriented thinking
Lập trình hướng đối tượng - p3
Giao trinh phuong phap tinh 1
10.khaosaths
OT HK II - 11
Www.mathvn.com 200 cau-khaosathamso2
Nmlt c13 con_tronangcao_in
Bdt dua ve mot bien
Lesson08
Ad

Viewers also liked (20)

PDF
PDF
InTechnology - Polycom VoIP Phone Handsets
PPT
Michanismoi aminas
PPTX
Jakobiinide diktatuur
PDF
1 [compatibility mode]
DOC
Practica 4
PDF
Caffarella letteraria
PPT
Trabajo epv
ODP
бодисын шинж чанар
PDF
La seguretat de les TIC a l'institut Torre del Palau de Terrassa
PPTX
Presentatie social media flickr
PPT
نسخ من بشار مهم
PPT
Texturas gráficas 1
ODS
цахим тест эрдэнэцэцэг
PPTX
Workshop Diigo @ ESE/IPSantarém
PPTX
المتباينات والمثلثات
ODP
мат 4 анги
XLSX
PPT
KONTENEDOREAK
InTechnology - Polycom VoIP Phone Handsets
Michanismoi aminas
Jakobiinide diktatuur
1 [compatibility mode]
Practica 4
Caffarella letteraria
Trabajo epv
бодисын шинж чанар
La seguretat de les TIC a l'institut Torre del Palau de Terrassa
Presentatie social media flickr
نسخ من بشار مهم
Texturas gráficas 1
цахим тест эрдэнэцэцэг
Workshop Diigo @ ESE/IPSantarém
المتباينات والمثلثات
мат 4 анги
KONTENEDOREAK
Ad

Similar to Sang tao4 (20)

DOC
Bài tập ôn lập trình
DOC
Bai tap java
DOCX
Bài tập CTDL và GT 8
PPT
giao trinh c++ Chuong1
PDF
C8 object-oriented thinking
PDF
CHƯƠNG 2.pdf
PPT
Ctdl C05
PPTX
Chương Trình Con
DOCX
Chuyen doi he so
PPT
Phương pháp nhánh cận
PDF
Exercise array
DOC
Lap trinh c++ có lời giải 3
PDF
Toan pt.de006.2012
PDF
[Vnmath.com] chuyen dh vinh lan 2 2015
DOC
Tích phân hàm phân thức hữu tỷ (part 2)
PDF
HÀM_NoCoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaapy.pdf
DOCX
HeuristicthuattoanAsaoooooooooooooooooo.docx
PDF
Toan pt.de111.2011
DOC
Toan d dh_2011
PDF
Chuong1 c
Bài tập ôn lập trình
Bai tap java
Bài tập CTDL và GT 8
giao trinh c++ Chuong1
C8 object-oriented thinking
CHƯƠNG 2.pdf
Ctdl C05
Chương Trình Con
Chuyen doi he so
Phương pháp nhánh cận
Exercise array
Lap trinh c++ có lời giải 3
Toan pt.de006.2012
[Vnmath.com] chuyen dh vinh lan 2 2015
Tích phân hàm phân thức hữu tỷ (part 2)
HÀM_NoCoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaapy.pdf
HeuristicthuattoanAsaoooooooooooooooooo.docx
Toan pt.de111.2011
Toan d dh_2011
Chuong1 c

Recently uploaded (20)

PPTX
CNDVBC - Chủ nghĩa duy vật biện chứng...
PPTX
Ứng dụng AI trong hệ thống cơ điện của nhóm 3.pptx
PDF
TỔNG QUAN KỸ THUẬT CDHA MẠCH MÁU.5.2025.pdf
PDF
f37ac936-c8c6-4642-9bc9-a9383dc18c15.pdf
PDF
bai giang an toan thong tin ke toan nam 2020
PDF
CHUYÊN ĐỀ DẠY THÊM HÓA HỌC LỚP 10 CẢ NĂM THEO FORM THI MỚI BGD - CÓ ÔN TẬP + ...
PDF
Cơ bản về matlab simulink cho người mới bắt đầu
PPTX
Bai 11.1 Tuan hoan mau.cấu tạo và vai trò pptx
PDF
Sách không hôi fyjj ịuk gtyi yu> ướt jiii iiij
PPT
danh-sach-lien-ket_Cấu trúc dữ liệu và giải thuậ.ppt
PPT
Slide Nguyên lý bảo hiểm đại học knh tế quốc dân
PDF
BÀI GIẢNG TÓM TẮT XÁC SUẤT THỐNG KÊ (KHÔNG CHUYÊN TOÁN, 2 TÍN CHỈ) - KHOA SƯ...
PPTX
Slide chương 3 môn thẩm định tài chính dự án
PDF
GIÁO TRÌNH ỨNG DỤNG CÔNG NGHỆ THÔNG TIN TRONG DẠY HỌC HÓA HỌC Ở TRƯỜNG PHỔ TH...
PPTX
CASE LÂM SÀNG MỤN TRỨNG CÁd (final, BS Vân Thanh)-SV tai lop.pptx
PDF
Giáo Dục Minh Triết: Tâm thế - Tâm thức bước vào kỷ nguyên mới
PDF
SÁNG KIẾN “MỘT SỐ KINH NGHIỆM HƯỚNG DẪN HỌC SINH THAM GIA CUỘC THI KHOA HỌC K...
PDF
CHUYÊN ĐỀ DẠY THÊM HÓA HỌC LỚP 11 CẢ NĂM THEO FORM THI MỚI BGD - CÓ ÔN TẬP + ...
PPTX
Triet hoc con nguoi va triet hoc thac si
PPTX
VNR202 LỊCH SỬ ĐẢNG, MÔN HỌC CẦN THIẾT C
CNDVBC - Chủ nghĩa duy vật biện chứng...
Ứng dụng AI trong hệ thống cơ điện của nhóm 3.pptx
TỔNG QUAN KỸ THUẬT CDHA MẠCH MÁU.5.2025.pdf
f37ac936-c8c6-4642-9bc9-a9383dc18c15.pdf
bai giang an toan thong tin ke toan nam 2020
CHUYÊN ĐỀ DẠY THÊM HÓA HỌC LỚP 10 CẢ NĂM THEO FORM THI MỚI BGD - CÓ ÔN TẬP + ...
Cơ bản về matlab simulink cho người mới bắt đầu
Bai 11.1 Tuan hoan mau.cấu tạo và vai trò pptx
Sách không hôi fyjj ịuk gtyi yu> ướt jiii iiij
danh-sach-lien-ket_Cấu trúc dữ liệu và giải thuậ.ppt
Slide Nguyên lý bảo hiểm đại học knh tế quốc dân
BÀI GIẢNG TÓM TẮT XÁC SUẤT THỐNG KÊ (KHÔNG CHUYÊN TOÁN, 2 TÍN CHỈ) - KHOA SƯ...
Slide chương 3 môn thẩm định tài chính dự án
GIÁO TRÌNH ỨNG DỤNG CÔNG NGHỆ THÔNG TIN TRONG DẠY HỌC HÓA HỌC Ở TRƯỜNG PHỔ TH...
CASE LÂM SÀNG MỤN TRỨNG CÁd (final, BS Vân Thanh)-SV tai lop.pptx
Giáo Dục Minh Triết: Tâm thế - Tâm thức bước vào kỷ nguyên mới
SÁNG KIẾN “MỘT SỐ KINH NGHIỆM HƯỚNG DẪN HỌC SINH THAM GIA CUỘC THI KHOA HỌC K...
CHUYÊN ĐỀ DẠY THÊM HÓA HỌC LỚP 11 CẢ NĂM THEO FORM THI MỚI BGD - CÓ ÔN TẬP + ...
Triet hoc con nguoi va triet hoc thac si
VNR202 LỊCH SỬ ĐẢNG, MÔN HỌC CẦN THIẾT C

Sang tao4

  • 1. Thi HSG Quốc gia 2010 CCKLK: Dãy con chung không liền kề dài nhất Cho dãy số nguyên dương x = (x1, x2, ..., xn). Dãy y = (xi1, xi2, ..., xik) được gọi là dãy con không liền kề của dãy x nếu 1  i1 < i21 < ... < ik1  n. Cho 2 dãy số nguyên a gồm n phần tử và b gồm m phần tử. Xác định chiều dài k của dãy con chung không liền kề dài nhất của a và b. 2  m, n  1000, 1 ai, bi  10000. CCKLK.INP CCKLK.OUT Giải thích 5 6 2 CCKLK.INP: Dòng đầu: n m. Từ dòng thứ hai trở đi: Dãy số a, 1 5 3 8 2 tiếp đến là dãy số b. 2 1 3 4 2 6 CCKLK.OUT: k. Thuật toán Quy hoạch động. Gọi d(i,j) là đáp số của bài toán khi xét hai dãy a[1..i] và b[1..j]. Ta có:  Nếu i ≤ 0 thì ta quy ước d(i,j) = 0,  Nếu a[i] = b[j] thì d(i,j) = d(i−2,j−2),  Nếu a[i] ≠ b[j] thì d(i,j) = max { d(i−1,j), d(i,j−1) }. Để cài đặt ta dùng 3 mảng một chiều x, y và z với ý nghĩa x[j] = d(i−2,j), y[j] = d(i−1,j) và z[j] = d(i,j). Khi đó hệ thức trên được viết là:  Nếu i ≤ 0 thì ta quy ước d(i,j) = 0,  Nếu a[i] = b[j] thì d(i,j) = d(i−2,j−2) ứng với z[j] = x[j−2],  Nếu a[i] ≠ b[j] thì d(i,j) = max { d(i−1,j), d(i,j−1) } ứng với z[j] = max { y[j], z[j−1] }. Muốn tránh các phép copy dữ liệu từ y sang x; từ z sang y và từ x sang z ta chỉ cần tráo đổi các con trỏ mảng. Độ phức tạp O(n.m) Chương trình Pascal (* CCKLK.PAS k: chieu dai day con chung khong lien ke dai nhat cua hai day so nguyen duong a[1..n], b[1..m] *) const fn = 'ccklk.inp'; gn = 'ccklk.out'; bl = #32; nl = #13#10; mn = 1001; type int = integer; mi1 = array[0..mn] of int; var n, m: int; a, b: mi1; function Max(a,b: int): int; begin if a >= b then Max := a else Max := b; end; procedure Doc; var i: int; f: text; begin assign(f,fn); reset(f); read(f,n, m);
  • 2. writeln(n,bl,m); for i := 1 to n do read(f,a[i]); for i := 1 to m do read(f,b[i]); write(nl, 'a: '); for i := 1 to n do write(a[i],bl); write(nl, 'b: '); for i := 1 to n do write(b[i],bl); close(f); end; procedure Ghi(k: int); var g: text; begin assign(g,gn); rewrite(g); writeln(g,k); close(g); end; (* d(i,j) = dap so cua bai toan voi a[1..i], b[1..j] d(i,j) = d(i-2,j-2) + 1, if a[i] = b[j] = Max begin d(i,j-1), d(i-1,j) end;, elsewhere = 0, if i < 1 || j < 1 *) function QHD: int; var i, j, v: int; c: array[1..3] of mi1; x, y, z, t: int; begin x := 1; y := 2; z := 3; { Init i = 0 } fillchar(c[x],sizeof(c[x]), 0); { Init i = 1 } c[y][0] := 0; v := 0; for j := 1 to m do begin if (a[1] = b[j]) then v := 1; c[y][j] := v; end; v := 0; for i := 2 to n do begin c[z][0] := 0; if (a[i] = b[1]) then v := 1; c[z][1] := v; for j := 2 to m do if (a[i] = b[j]) then c[z][j] := c[x][j-2]+1 else c[z][j] := Max(c[z][j-1],c[y][j]); t := x; x := y; y := z; z := t; end; QHD := c[y][m]; end; BEGIN Doc; Ghi(QHD); write(nl,' Fini ');
  • 3. readln; END. Chương trình CPP /* DevC++ CCKLK.CPP k: chieu dai day con chung khong lien ke dai nhat cua hai day so nguyen duong a[1..n], b[1..m] */ #include <fstream> #include <iostream> using namespace std; // D A T A A N D V A R I A B L E const char * fn = "ccklk.inp"; const char * gn = "ccklk.out"; const int mn = 1001; int n, m; int a[mn],b[mn]; // P R O T O T Y P E S void Doc(); int QHD(); // Quy hoach dong void Ghi(int); int Max(int,int); // I M P L E M E N T A T I O N int main() { Doc(); Ghi(QHD()); cout << endl << " Fini "; cin.get(); return 0; } int Max(int a, int b) { return (a >= b) ? a : b; } void Doc() { int i; ifstream f(fn); f >> n >> m; cout << endl << n << " " << m; for (i = 1; i <= n; ++i) f >> a[i]; for (i = 1; i <= m; ++i) f >> b[i]; cout << endl << "a: "; for (i = 1; i <= n; ++i) cout << a[i] << " "; cout << endl << "b: "; for (i = 1; i <= m; ++i) cout << b[i] << " "; f.close(); } void Ghi(int k) { ofstream g(gn); g << k; g.close(); } /* d(i,j) = dap so cua bai toan voi a[1..i], b[1..j] d(i,j) = d(i-2,j-2) + 1, if a[i] = b[j] = Max { d(i,j-1), d(i-1,j) }, elsewhere
  • 4. = 0, if i < 1 || j < 1 */ int QHD() { int *x, *y, *z, *t; int i, j , m1 = m+1, v ; x = new int[m1]; y = new int[m1]; z = new int[m1]; // Init i = 0 memset(x,0,sizeof(int)*m1); // Init i = 1 y[0] = 0; v = 0; for (j = 1; j <= m; ++j) { if (a[1] == b[j]) v = 1; y[j] = v; } v = 0; for (i = 2; i <= n; ++i) { z[0] = 0; if (a[i]==b[1]) v = 1; z[1] = v; for (j = 2; j <= m; ++j) z[j] = (a[i] == b[j]) ? x[j-2]+1 : Max(z[j-1],y[j]); t = x; x = y; y = z; z = t; } v = y[m]; delete x; delete y; delete z; return v; } Ổn định Cho đồ thị có hướng gồm n đỉnh và m cung (u,v). Cho trước đỉnh s. Một đỉnh d ≠ s được gọi là ổn định đối với s nếu có ít nhất hai đường đi ngắn nhất từ s tới d. Hãy tính k là số lượng đỉnh ổn định đối với đỉnh s. 2  n  10000, 1  m  50000. ONDINH.INP ONDINH.OUT Giải thích 6 7 1 2 ONDINH.INP: Dòng đầu: n m s. Từ dòng thứ hai trở đi:m cung 1 2 dạng u v. Có thể có các cung trùng nhau (dư thừa). 1 4 ONDINH.OUT: k. 2 3 Thí dụ cho biết: có 3 đỉnh ổn định đối với đỉnh 1 là các đỉnh 5 2 5 và 6. 4 5 1 → 2 → 5; 1 → 4→ 5; 2 3 1 → 2 → 5→ 6; 1 → 4→ 5→ 6. 5 6 Thuật toán Dùng một biến thể của thuật toán Dijkstra. // Ondinh.CPP // HSG 2010 #include <string.h>
  • 5. #include <fstream> #include <iostream> //#include <mem.h> using namespace std; // D A T A A N D V A R I A B L E S char * fn = "ondinh.inp"; char * gn = "ondinh.out"; const int mn = 10001; const int mm = 50001; typedef struct { int a, b; } cung; cung c[mm]; int len[mn]; // len[i] chieu dai s => i char mark[mn]; // mark[i] danh dau dinh i: // Chua xet 0; Co trong hang doi q 1; Da xu li 2 int d[mn]; // d[i] so luong duong ngan nhat s => i int q[mn]; // hang doi int n, m, s ; // so dinh n, so cung m, dinh xuat phat s // P R O T O T Y P E S int main(); void Doc(); int XuLi(); int Min(int, int); int BinSearch(cung [], int, int, int); int Sanh(int,int,int,int); void Ghi(); int main(){ Doc(); XuLi(); Ghi(); cout << endl << " Fini"; cin.get(); return 0; } int Min(int a, int b) { return (a <= b) ? a : b; } void Ghi() { int i, k = 0; for (i = 1; i <= n; ++i) if (d[i] > 1) ++k; ofstream g(gn); g << k; g.close(); } int XuLi() { int i, j, k, v, r; v = 0; r = 0; memset(mark,0,sizeof(mark)); memset(d,0,sizeof(d)); len[s] = 0; d[s] = 1; q[++v] = s; while (r < v) { // 1 i = q[++r]; cout << endl << " Xet dinh " << i; mark[i] = 2; for (k = BinSearch(c,m,i,0); c[k].a == i; ++k) { // 2
  • 6. j = c[k].b; // xet cac dinh j ke dinh i if (mark[j] == 0) { len[j] = len[i]+1; mark[j] = 1; d[j] = d[i]; q[++v] = j; } else if (mark[j] == 1) { if (len[i]+1 == len[j]) d[j]++; } } // 2 } // 1 for (i = 1; i <= n; ++i) cout << d[i] << " "; } int Sanh(int u1, int v1, int u2, int v2) { if (u1 < u2) return -1; if (u1 > u2) return 1; // u1 == u2 if (v1 < v2) return -1; if (v1 > v2) return 1; return 0; } int BinSearch(cung c[], int k, int u, int v) { int d = 1, m; while (d < k) { m = (d + k) / 2; if (Sanh(c[m].a,c[m].b,u,v) < 0) d = m+1; else k = m; } return d; } void Doc() { int i, j, u, v, k = 0; ifstream f(fn); f >> n >> m >> s; cout << endl << n << " " << m << " " << s; // The first edge (u,v) f >> u >> v; ++k; c[k].a = u; c[k].b = v; for (i = 2; i <= m; ++i) { f >> u >> v; j = BinSearch(c,k,u,v); if (Sanh(c[j].a,c[j].b,u,v) != 0) { if (Sanh(c[j].a,c[j].b,u,v) < 0) { ++k; c[k].a = u; c[k].b = v; } else { memmove(&c[j+1], &c[j], (k-j+1)*sizeof(cung));// <mem.h> c[j].a = u; c[j].b = v; ++k; } } } f.close(); m = k; }
  • 7. // Ondinh.CPP Phuong an cu // HSG 2010 #include <string.h> #include <fstream> #include <iostream> //#include <mem.h> using namespace std; // D A T A A N D V A R I A B L E S char * fn = "ondinh.inp"; char * gn = "ondinh.out"; const int mn = 10001; const int mm = 50001; typedef struct { int a, b; } cung; cung c[mm]; int p[mn]; char d[mn]; int s[mn]; int n, m, x; // so dinh n, so cung m, dinh xuat phat x // P R O T O T Y P E S int main(); void Doc(); int XuLi(); int Minp(); int Min(int, int); int BinSearch(cung [], int, int, int); int Sanh(int,int,int,int); void Ghi(); int main(){ Doc(); XuLi(); Ghi(); cout << endl << " Fini"; cin.get(); return 0; } int Min(int a, int b) { return (a <= b) ? a : b; } int Minp() { int i, imin = 0; for (i = 1; i <= n; ++i) if (!d[i] && p[i] < p[imin]) imin = i; d[imin] = 1; return imin; } void Ghi() { int i, k = 0; for (i = 1; i <= n; ++i) if (s[i] > 1) ++k; ofstream g(gn); g << k; g.close(); }
  • 8. int XuLi() { int i, imin, j, k; memset(d,0,sizeof(d)); for (i = 2; i <= n; ++i) s[i] = 0; s[x] = 1; p[0] = n+2; p[x] = 0; for (i = 2; i <= n; ++i) p[i] = n+1; for (i = 1; i <= n; ++i) { imin = Minp(); for (k = BinSearch(c,m,imin,0); c[k].a == imin; ++k) { j = c[k].b; if (!d[j]) { if (p[imin]+1 < p[j]) { p[j] = p[imin]+1; s[j] = s[imin]; } else if (p[imin]+1 == p[j]) ++s[j]; } } } } int Sanh(int u1, int v1, int u2, int v2) { if (u1 < u2) return -1; if (u1 > u2) return 1; // u1 == u2 if (v1 < v2) return -1; if (v1 > v2) return 1; return 0; } int BinSearch(cung c[], int k, int u, int v) { int d = 1, m; while (d < k) { m = (d + k) / 2; if (Sanh(c[m].a,c[m].b,u,v) < 0) d = m+1; else k = m; } return d; } void Doc() { int i, j, u, v, k = 0; ifstream f(fn); f >> n >> m >> x; // The first edge (u,v) f >> u >> v; ++k; c[k].a = u; c[k].b = v; for (i = 2; i <= m; ++i) { f >> u >> v; j = BinSearch(c,k,u,v); if (Sanh(c[j].a,c[j].b,u,v) != 0) { if (Sanh(c[j].a,c[j].b,u,v) < 0) { ++k; c[k].a = u; c[k].b = v; } else { memmove(&c[j+1], &c[j], (k-j+1)*sizeof(cung));// <mem.h> c[j].a = u; c[j].b = v; ++k; } } } f.close();
  • 9. m = k; } Mã số thuế Xét tập S gồm tất cả các số 1..n trong hệ 36, 36  n  1016. Cho số m: 3  m  70. Xét dãy số nguyên 1 < c1 < c2 < ... < ck < 36, k = (m1)/2 , x là số nguyên lớn nhất không vượt quá x. Chọn các số chứa các chữ số < c1 cấp cho 2 nhóm 1 và 2 rồi xóa các số này. Chọn các số chứa các chữ số < c2 cấp cho 2 nhóm 3, 4.... Các số còn lại cấp cho 1 hoặc 2 nhóm cuối. Nhóm le: từ nhỏ, nhóm chẵn: từ lớn. Cho các số hệ 10: n, m, ci, p và q. Xác định mã (hệ 36) cấp cho ng thứ q nhóm p. Thí dụ: n = 50, m = 3, p = 2, q = 2, c1 = 16. 1d Căt đoạn Cho hình chữ nhật OABC, OA = n, OC = m, coi O là gốc tọa độ (0,0). Trong hình CN cho k đoạn thẳng đứng. Tìm điểm P trên BA hoặc BC để đoạn OP cắt nhiều đoạn nhất. Đoạn khác nhau Cho dãy a gồm n số nguyên dương. Một đoạn của dãy a, kí hiệu a[i..j] là dãy gồm các phần tử đứng liên tiếp nhau trong dãy a, kể từ phần tử ai đến phần tử aj, a[i..j] = (ai, ai+1,...,aj-1, aj), 1  i <= j  n. Hãy tìm đoạn dài nhất gồm các phần tử đôi một khác nhau. 1  n, ai  100000. Dữ liệu vào: Tệp văn bản diff.inp  Dòng đầu tiên: số n.  Từ dòng thứ hai trở đi: dãy số a. Dữ liệu ra: Tệp văn bản diff.out chứa 2 số:  imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a  dmax  số phần tử của doạn dài nhất. Các số trên cùng dòng cách nhau qua dấu cách. diff.inp diff.out Giải thích Tính từ phần tử thứ 3 có cả thảy 10 3 8 số đôi một khác nhau là 5 2 4 1 5 8 3 2 7 6 9 4 1 5 3 2 7 6 9 Thuật toán Lần lượt đọc các phần tử ai của dãy a và đánh dấu vị trí xuất hiện của ai trong dãy thông qua mảng p, p[ai] = i. Với thí dụ đã cho, sau khi đọc và xử lý xong dãy a ta phải thu được i 1 2 3 4 5 6 7 8 9 10 ai 5 2 4 1 5 3 2 7 6 9 p 4 2/7 6 3 1/5 9 8 0 10 0
  • 10. p[2] = 2/7 cho biết số 2 lúc đầu xuất hiện tại vị trí 2 trong dãy a, sau đó xuất hiện tại vị trí 7 trong dãy a. p[8] = p[10] = 0 cho biết các số 8 và 10 không xuất hiện trong dãy a. Ta gọi p là dãy trỏ ngược hay dãy vị trí của dãy a. Ta xử lý từng đoạn d của ai như sau. Mỗi đoạn d sẽ bao gồm một dãy liên tiếp các phần tử đôi một khác nhau tính từ chỉ số i đến j. Thí dụ trên cho ta lần lượt 3 đoạn sau:  Đoạn thứ nhất d = a[1..4] = (5, 2, 4, 1), i = 1, j = 4,  Đoạn thứ hai d = a[2..6] = (2, 4, 1, 5, 3), i = 2, j = 6,  Đoạn thứ ba d = a[3..10] = (4, 1, 5, 3, 2, 7, 6, 9), i = 3, j = 10. Mỗi đoạn d được xác định như sau: Mỗi khi gặp phần tử aj đầu tiên trùng với một phần tử trong dãy tính từ i thì ta cắt ra được đoạn d = a[i..j1]. Với mỗi đoạn d[i..j] ta tính số phần tử của đoạn đó là ji+1 và cập nhật giá trị dmax. Để khởi trị cho đoạn tiếp theo, ta đặt i = p[aj]+1. Chú ý rằng p[aj] là vị trí xuất hiện của giá trị lặp aj. Độ phức tạp O(n) Chương trình Pascal (* diff.pas Tim doan dai nhat gom cac phan tu doi mot khac nhau *) const fn = 'diff.inp'; gn = 'diff.out'; bl = #32; nl = #13#10; mn = 100001; var p: array[0..mn] of longint; n: longint; imax, dmax: longint; { imax - chi so dau tien cua doan dai nhat dmax - so phan tu cua doan dai nhat } f,g: text; procedure Run; var i, j, v, istart: longint; begin imax := 0; dmax := 0; fillchar(p,sizeof(p),0); assign(f,fn); reset(f); readln(f,n); read(f,v); { phan tu dau tien trong day } p[v] := 1; istart := 1; for i := 2 to n do begin read(f,v); if (p[v] >= istart) then begin if (i-istart > dmax)then begin dmax := i-istart; imax := istart; end; istart := p[v]+1;
  • 11. end; p[v] := i; end; close(f); i := n+1; if (i-istart > dmax) then begin dmax := i-istart; imax := istart; end; assign(g,gn); rewrite(g); write(g, imax, nl, dmax); close(g); end; BEGIN Run; write(nl, ' Fini '); readln; END. Chương trình CPP /* DevC++: Diff.cpp Tim doan dai nhat gom cac phan tu doi mot khac nhau */ #include <fstream> #include <iostream> using namespace std; // D A T A A N D V A R I A B L E S const char * fn = "diff.inp"; const char * gn = "diff.out"; const int mn = 100001; int p[mn]; // p[v] = i: noi xuat hien so v trong day int n; int imax; // chi so dau tien cua doan dai nhat int dmax; // so phan tu cua doan dai nhat // P R O T O T Y P E S int main(); void Run(); // I M P L E M E N T A T I O N int main() { Run(); cout << endl; system("PAUSE"); return EXIT_SUCCESS; } void Run() { int i, j, v, istart; imax = 0, dmax = 0;
  • 12. memset(p,0,sizeof(p)); ifstream f(fn); f >> n; f >> v; // v phan tu dau day p[v] = 1; istart = 1; for (i = 2; i <= n; ++i) { f >> v; if (p[v] >= istart) { // so v co trong doan istart..i if (i-istart > dmax) { dmax = i-istart; imax = istart; } istart = p[v]+1; } p[v] = i; } f.close(); i = n+1; if (i-istart > dmax) { dmax = i-istart; imax = istart; } ofstream g(gn); g << imax << endl << dmax; } Hoán vị 1k Cho dãy a gồm n số nguyên dương đôi một khác nhau. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành một hoán vị của dãy (1, 2 ,..., k), 1  n, ai  100000. Dữ liệu vào: Tệp văn bản hv1k.inp  Dòng đầu tiên: số n.  Từ dòng thứ hai trở đi: dãy số a. Dữ liệu ra: Tệp văn bản hv1k.out chứa 2 số:  imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a  dmax  số phần tử của đoạn dài nhất. Các số trên cùng dòng cách nhau qua dấu cách. hv1k.inp hv1k.out Giải thích 10 3 Tính từ phần tử thứ 3 có 6 số 5 2 4 1 5 6 liên tiếp tạo thành một hoán vị 3 2 6 9 8 của 1..6 là 4 1 5 3 2 6 Thuật toán Kí hiệu a[i..j] là đoạn gồm các phần tử liên tiếp từ ai đến aj của dãy a và kí hiệu set(x) là tập chứa các phần tử (khác nhau) của dãy x. Với thí dụ đã cho ta có, a[1..5] = (5, 2, 4, 1, 5) và do đó set(a[1..5]) = {1, 2, 4, 5}. Gọi p là dãy vị trí (trỏ ngược) của dãy a. Vì dãy a gồm các phần tử đôi một khác nhau nên p cũng chứa các chỉ số đôi một khác nhau. Khi đó a chứa một đoạn là hoán vị của k số tự nhiên đầu tiên (1,2,...,k) khi và chỉ khi tìm được hai chỉ số s và e thỏa đồng thời hai tính chất sau:  es+1 = k, và  set(a[s..e]) = {1, 2, ... , k}.
  • 13. Trong thí dụ trên ta tìm được s = 3, e = 8, k = 6, a[3..8] = (4, 1, 5, 3, 2, 6), và do đó set(a[3..8]) = {1, 2, 3, 4, 5, 6}. Xét dãy chỉ số 1, 2, ..., i thỏa tính chất  j: 1  j  i: p[j] ≠ 0. Để ý rằng điều kiện p[j] ≠ 0 tương đương với điều kiện giá trị j của dãy a xuất hiện tại vị trí p[j]. Đặt s = min p[1..i] = min {p[1], p[2], ... , p[i]} và e = max p[1..i] = max {p[1], p[2], ... , p[i]}. Ta thấy a chứa một đoạn là hoán vị của dãy (1,2,...,i) khi và chỉ khi es+1 = i. Độ phức tạp O(n). Chương trình Pascal (* -------------------------------------- hv1k.pas Tim doan dai nhat trong day so doi mot khac nhau tao thanh mot hoan vi 1..k -----------------------------------------*) const fn = 'hv1k.inp'; gn = 'hv1k.out'; mn = 1000002; nl = #13#10; bl = #32; var p: array[0..mn] of longint; n: longint; imax, dmax: longint; f,g: text; procedure Doc; var i, v: longint; begin assign(f,fn); reset(f); fillchar(p,sizeof(p),0); readln(f,n); for i := 1 to n do begin read(f,v); p[v] := i; end; close(f); end; function Min(a,b: longint): longint; begin if (a <= b) then Min := a else Min := b end; function Max(a,b: longint): longint; begin if (a >= b) then Max := a else Max := b end; procedure Hv; var i, pmin, pmax: longint; begin pmin := n+1; pmax := 0; imax := 0; dmax := 0; for i := 1 to n do begin if (p[i] = 0) then break;
  • 14. pmin := Min(pmin,p[i]); pmax := Max(pmax,p[i]); if (pmax - pmin + 1 = i) then begin imax := pmin; dmax := pmax - pmin + 1; end; end; end; procedure Ghi; begin assign(g,gn); rewrite(g); writeln(g, imax,nl,dmax); close(g); end; procedure Run; begin Doc; Hv; Ghi; end; BEGIN Run; write(nl,' Fini '); readln; END. Chương trình CPP /* -------------------------------------- devCPP: hv1k.cpp Tim doan dai nhat trong day so doi mot khac nhau tao thanh mot hoan vi 1..k -----------------------------------------*/ #include <fstream> #include <iostream> using namespace std; // D A T A A N D V A R I A B L E S const char * fn = "hv1k.inp"; const char * gn = "hv1k.out"; const int mn = 100002; int p[mn]; int n; int imax, dmax; // P R O T O T Y P E S int main(); void Doc(); int Min(int, int); int Max(int, int); void Hv(); void Ghi();
  • 15. void Run(); // I M P L E M E N T A T I O N int main() { Run(); cout << endl; system("PAUSE"); return EXIT_SUCCESS; } void Doc() { int i, v; ifstream f(fn); memset(p,0,sizeof(p)); f >> n; for (i = 1; i <= n; ++i) { f >> v; p[v] = i; } f.close(); } int Min(int a, int b) { return (a <= b) ? a : b; } int Max(int a, int b) { return (a >= b) ? a : b; } void Hv() { int i, pmin = n+1, pmax = 0; imax = 0; dmax = 0; for (i = 1; i <= n; ++i) { if (p[i] == 0) break; pmin = Min(pmin,p[i]); pmax = Max(pmax,p[i]); if (pmax - pmin + 1 == i) { imax = pmin; dmax = pmax - pmin + 1; } } } void Ghi() { ofstream g(gn); g << imax << endl << dmax; g.close(); } void Run() { Doc(); Hv(); Ghi(); } Hoán vị sk Cho dãy a gồm n số nguyên dương đôi một khác nhau. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành một hoán vị của dãy số tự nhiên liên tiếp (s, s+1 ,..., k), 1  n, ai  100000.
  • 16. Dữ liệu vào: Tệp văn bản hvsk.inp  Dòng đầu tiên: số n.  Từ dòng thứ hai trở đi: dãy số a. Dữ liệu ra: Tệp văn bản hvsk.out chứa 2 số:  imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a  dmax  số phần tử của đoạn dài nhất. Các số trên cùng dòng cách nhau qua dấu cách. hvsk.inp hvsk.out Giải thích 7 3 Tính từ phần tử thứ 3 trong dãy 12 1 4 7 8 5 a có 5 số liên tiếp tạo thành một 5 6 hoán vị của 4..8 là a[3..7]: 4 7 8 5 6 Thuật toán Gọi p là dãy trỏ ngược của dãy a. Vì dãy a gồm các phần tử đôi một khác nhau nên p cũng chứa các chỉ số đôi một khác nhau. Khi đó a chứa một đoạn là hoán vị của dãy số tự nhiên liên tiếp s..k khi và chỉ khi tìm được hai chỉ số i và j thỏa đồng thời hai tính chất sau:  ji = ks, và  set(a[i..j]) = {s, s+1, ... , k}. Trong thí dụ trên ta tìm được i = 3, j = 7, s = 4, k = 8, a[3..7] = (4, 7, 8, 5, 6), và do đó set(a[3..7]) = {4, 5, 6, 7, 8}. Khi đọc dữ liệu ta đồng thời xác định hai giá trị cận vmin và vmax của dãy a. Để ý rằng p[vmin..vmax] gồm các đoạn toàn 0 và đoạn khác 0 đan xen nhau. Thí dụ trên cho ta ai 1 2 3 4 5 6 7 8 9 10 11 12 p 2 0 0 3 6 7 4 5 0 0 0 1 vmin = 1. vmax = 12 Khi duyệt p[vmin..vmax] ta xét 2 trạng thái 0 và 1 như sau: Trạng thái 0: Duyệt đoạn p toàn 0, p[2..3] và p[9..11]. Nếu gặp p[i] > 0 thì khởi trị cho trạng thái duyệt đoạn khác 0, p[istart..]:  Ghi nhận chỉ số đầu đoạn khác 0: istart = i,  Khởi trị cac giá trị pmin = min p[istart..k1] và pmax = max p[istart..k1] cho đoạn này: pmin = pmax = p[i],  Chuyển qua trạng thái 1. Trạng thái 1: Duyệt đoạn p khác 0, p[1..1], p[4..8] và p[12..12]. Nếu p[i] > 0 thì cập nhật các chỉ số pmin và pmax. Kiểm tra đẳng thức pmaxpmin = iistart để cập nhật chỉ số đầu tiên của đoạn hoán vị trong dãy a, imax và chiều dài của đoạn hoán vị, dmax. Nếu p[i] = 0 thì kết thúc việc duyệt đoạn p[isstart..i1] này, chuyển qua trạng thái 0. Độ phức tạp O(n).
  • 17. Chương trình Pascal Chương trình CPP /* -------------------------------------- hvsk.cpp Tim doan dai nhat trong day so doi mot khac nhau tao thanh mot hoan vi cua day so tu nhien lien tiep s, s+1,...,k -----------------------------------------*/ #include <fstream> #include <iostream> using namespace std; // D A T A A N D V A R I A B L E S const char * fn = "hvsk.inp"; const char * gn = "hvsk.out"; const int mn = 100002; int p[mn]; int pmin, pmax; int vmin, vmax; int n; int imax, dmax; // P R O T O T Y P E S int main(); void Doc(); int Min(int, int); int Max(int, int); void Hv(); // Sliding window void Ghi(); void Run(); // I M P L E M E N T A T I O N int main() { Run(); cout << endl; system("PAUSE"); return EXIT_SUCCESS; } void Doc() { int i, v; ifstream f(fn); memset(p,0,sizeof(p)); vmin = mn; vmax = 0; f >> n; for (i = 1; i <= n; ++i) { f >> v; p[v] = i; vmin = Min(vmin,v); vmax = Max(vmax,v);
  • 18. } f.close(); } int Min(int a, int b) { return (a <= b) ? a : b; } int Max(int a, int b) { return (a >= b) ? a : b; } void Hv() { int i, istart, q = 0; // trang thai q dmax = 0; ++vmax; for (i = vmin; i <= vmax; ++i) { switch(q) { case 0: // duyet doan toan 0 if (p[i] > 0) { // Khoi tri khi gap so khac 0 pmin = pmax = p[i]; istart = i; q = 1; } break; case 1: // duyet doan khac 0 if (p[i] > 0) { pmin = Min(pmin,p[i]); pmax = Max(pmax,p[i]); if (pmax-pmin == i-istart && pmax-pmin+1 > dmax){ dmax = pmax-pmin+1; imax = pmin; } } else q = 0; break; } // end switch } // end for } void Ghi() { ofstream g(gn); g << imax << endl << dmax; g.close(); } void Run() { Doc(); Hv(); Ghi(); } Hoán vị 1k dài nhất IOIcamp Marathon 2005-2006 Cho dãy a gồm n số nguyên dương. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành một hoán vị của dãy số tự nhiên 1..k; 1  n, ai  100000. Dữ liệu vào: Tệp văn bản hv1kmax.inp  Dòng đầu tiên: số n.
  • 19. Từ dòng thứ hai trở đi: dãy số a. Dữ liệu ra: Tệp văn bản hv1kmax.out chứa 2 số:  imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a  dmax  số phần tử của đoạn dài nhất. Các số trên cùng dòng cách nhau qua dấu cách. hv1kmax.inp hv1kmax.out Giải thích 10 3 Tính từ phần tử thứ 3 trong dãy 12 1 4 1 5 5 a có 5 số liên tiếp tạo thành một 3 2 3 1 2 hoán vị của 1..5 là a[3..7]: 4 1 5 3 2 Thuật toán Duyệt dãy, dựa theo bài diff, xác định từng đoạn ứng viên a[d..c] chứa tối đa các phần tử liên tiếp nhau trong dãy a và đôi một khác nhau. Với mỗi đoạn ứng viên, dựa theo bài Hv1k, gọi thủ tục Hv(d,c) xác định và cập nhật đoạn dài nhất trong a[d..c] tạo thành một hoán vị của dãy số tự nhiên 1..k. Độ phức tạp Chương trình Pascal (* hv1kmax.pas: Tim doan dai nhat gom cac phan tu tao thanh mot hoan vi cua 1..k *) const fn = 'hv1kmax.inp'; gn = 'hv1kmax.out'; mn = 100002; bl = #32; nl = #13#10; var p: array[0..mn] of longint; { p[v] - noi xuat hien gia tri v trong day } n: longint; imax, dmax: longint; f, g: text; function Min(a,b: longint): longint; begin if (a <= b) then Min := a else Min := b; end; function Max(a,b: longint): longint; begin if (a >= b) then Max := a else Max := b; end; procedure Hv(d,c: longint); var i, pmin, pmax: longint; begin pmin := c+1; pmax := d-1; for i := 1 to c-d+1 do begin if (p[i] < d ) or (p[i] > c) then break; pmin := Min(pmin,p[i]); pmax := Max(pmax,p[i]); if (i > dmax) then begin if (pmax - pmin + 1 = i) then
  • 20. begin imax := pmin; dmax := i; end end end; end; procedure Ghi; begin assign(g,gn); rewrite(g); writeln(g,imax, nl, dmax); close(g); end; procedure Run; var i, v , istart: longint; begin fillchar(p,sizeof(p),0); imax := 0; dmax := 0; assign(f,fn); reset(f); readln(f,n); read(f,v); { v – phan tu dau day a } p[v] := 1; istart := 1; { chi so dau doan } for i := 2 to n do begin read(f,v); if (p[v] >= istart) then { gap phan tu trung lap } begin if (i-istart > dmax) then Hv(istart, i-1); istart := p[v]+1; { Khoi tri dau doan moi } end; p[v] := i; end; close(f); if (n+1-istart > dmax) then Hv(istart,n); Ghi; end; BEGIN Run; write(nl,' Fini '); readln; END. Chương trình CPP /* DevC++: hv1kmax.cpp Tim doan dai nhat gom cac phan tu tao thanh mot hoan vi cua 1..k */ #include <fstream> #include <iostream> using namespace std; // D A T A A N D V A R I A B L E S
  • 21. const char * fn = "hv1kmax.inp"; const char * gn = "hv1kmax.out"; const int mn = 100002; int p[mn]; // p[v] - noi xuat hien gia tri v trong day int n; int imax, dmax; // P R O T O T Y P E S int main(); void Run(); int Min(int, int); int Max(int, int); void Hv(int, int); // Sliding window void Ghi(); // I M P L E M E N T A T I O N int main() { Run(); cout << endl; system("PAUSE"); return EXIT_SUCCESS; } int Min(int a, int b) { return (a <= b) ? a : b; } int Max(int a, int b) { return (a >= b) ? a : b; } // tim hoan vi 1..n trong doan // gom cac phan tu khac nhau doi mot void Hv(int d, int c) { int i, pmin = c+1, pmax = d-1; int n = c-d+1; for (i = 1; i <= n; ++i) { if (p[i] < d || p[i] > c) break; pmin = Min(pmin,p[i]); pmax = Max(pmax,p[i]); if (i > dmax) { if (pmax - pmin + 1 == i) { imax = pmin; dmax = i; } } } } void Ghi() { ofstream g(gn); g << imax << endl << dmax; g.close(); } void Run() { int i, v , istart; memset(p,0,sizeof(p)); imax = 0; dmax = 0;
  • 22. ifstream f(fn); f >> n; f >> v; p[v] = 1; istart = 1; for (i = 2; i <= n; ++i) { f >> v; if (p[v] >= istart) { if (i-istart > dmax) Hv(istart, i-1); istart = p[v]+1; } p[v] = i; } f.close(); if (n+1-istart > dmax) Hv(istart,n); Ghi(); } Hoán vị sk dài nhất Cho dãy a gồm n số nguyên dương. Hãy tìm đoạn dài nhất gồm các phần tử tạo thành một hoán vị của dãy số nguyên dương liên tiếp s, s+1,...,k; 1  n, ai  100000. Dữ liệu vào: Tệp văn bản hvskmax.inp  Dòng đầu tiên: số n.  Từ dòng thứ hai trở đi: dãy số a. Dữ liệu ra: Tệp văn bản hvskmax.out chứa 2 số:  imax  chỉ số đầu tiên của đoạn dài nhất tìm được trong dãy a  dmax  số phần tử của đoạn dài nhất. Các số trên cùng dòng cách nhau qua dấu cách. hvskmax.inp hvskmax.out Giải thích 10 3 Tính từ phần tử thứ 3 trong dãy 5 1 9 7 5 5 a có 5 số liên tiếp tạo thành một 8 6 3 9 2 hoán vị của 5..9 là a[3..7]: 9 7 5 8 6 Thuật toán Pha 1. Đọc dãy a, với mỗi ai ghi nhận p[ai] = i là vị trí xuât hiện giá trị ai. Gọi vmin và vmax lần lượt là giá trị min và max của dãy a. Pha 2. Duỵệt dãy giá trị i = vmin..vmax tạo thành một đoạn d gồm các giá trị xuất hiện liên tiếp trong dãy a, xác định các giá trị pmin và pmax là vị trí xuất hiện đầu tiên và cuối cùng trong đoạn d. Đoạn d là một hoán vị của dãy số nguyên dương liên tiếp i, i+1,...,s khi và chỉ khi pmaxpmin+1 = si. Độ phức tạp Chương trình Pascal Chương trình CPP /* DevC++: hv1kmax.cpp Tim doan dai nhat gom cac phan tu tao thanh mot hoan vi cua s..k */
  • 23. #include <fstream> #include <iostream> using namespace std; // D A T A A N D V A R I A B L E S const char * fn = "hvskmax.inp"; const char * gn = "hvskmax.out"; const int mn = 100002; int p[mn]; // p[v] - noi xuat hien gia tri v trong day int a[mn]; int n; int imax, dmax; // P R O T O T Y P E S int main(); void Run(); int Min(int, int); int Max(int, int); void Hv(int, int); // Sliding window void Ghi(); // I M P L E M E N T A T I O N int main() { Run(); cout << endl; system("PAUSE"); return EXIT_SUCCESS; } int Min(int a, int b) { return (a <= b) ? a : b; } int Max(int a, int b) { return (a >= b) ? a : b; } void Hv(int d, int c) { int i, istart, q = 0, pmin = c+1, pmax = d-1; int vmin, vmax; // trang thai q vmin = vmax = a[d]; for (i = d+1; i <= c; ++i) { vmin = min(vmin,a[i]); vmax = max(vmax,a[i]); } for (i = vmin; i <= vmax; ++i) { switch(q) { case 0: // duyet doan toan 0 if (p[i] > 0) { // Khoi tri khi gap so khac 0 pmin = pmax = p[i]; istart = i; q = 1; } break; case 1: // duyet doan khac 0 if (p[i] > 0) { pmin = Min(pmin,p[i]);
  • 24. pmax = Max(pmax,p[i]); if (pmax-pmin == i-istart && pmax-pmin+1 > dmax){ dmax = pmax-pmin+1; imax = pmin; } } else q = 0; break; } // end switch } // end for } void Ghi() { ofstream g(gn); g << imax << endl << dmax; g.close(); } void Run() { int i, v , istart; memset(p,0,sizeof(p)); imax = 0; dmax = 0; ifstream f(fn); f >> n; f >> v; a[1] = v; p[v] = 1; istart = 1; for (i = 2; i <= n; ++i) { f >> v; a[i] = v; if (p[v] >= istart) { if (i-istart > dmax) Hv(istart, i-1); istart = p[v]+1; } p[v] = i; } f.close(); if (n+1-istart > dmax) Hv(istart,n); Ghi(); } Đoạn nguyên tố Cho n số tự nhiên ai i = 1..n. Hãy tìm đoạn dài nhất gồm các phần tử là các số nguyên tố. 1  n, ai  100000. /* --------------------------------------- Primes.cpp Tim doan dai nhat gom cac so nguyen to ----------------------------------------*/ #include <fstream> #include <iostream> #include <math.h> using namespace std; // D A T A A N D V A R I A B L E S const int mn = 100001; const char * fn = "primes.inp";
  • 25. char p[mn]; // p[i] = 0=> i nguyen to int n; int imax, dmax; // P R O T O T Y P E S void Run(); void Sang(int); void Duyet(); // I M P L E M E N T A T I O N int main() { Run(); cout << endl << " Fini "; cin.get(); return 0; } void Sang(int n) { int can = (int) sqrt(n); int i, j, k; p[0] = p[1] = 1; for (i = 2; i <= can; ++i) if (p[i]==0) for (k = n/i, j = i; j <= k; ++j) p[i*j] = 1; } void Duyet() { int i, x, d; imax = 0; dmax = 0; d = 0; ifstream f(fn); f >> n; for (i = 1; i <= n; ++i) { f >> x; cout << x << " "; if (p[x]) { // x khong nguyen to if (d > dmax) { dmax = d; imax = i; } d = 0; } else ++d; } f.close(); if (d > dmax) { dmax = d; imax = n+1; } imax = imax - dmax; } void Run() { Sang(mn); Duyet(); cout << endl << imax << " " << dmax; } Đoạn nguyên tố cùng nhau
  • 26. Cho n số tự nhiên ai i = 1..n. Hãy tìm đoạn dài nhất gồm các phần tử, trong đó hai phần tử kề nhau thì nguyên tố cùng nhau. 1  n, ai  100000. /* --------------------------------------- Primes.cpp Tim doan dai nhat gom cac so nguyen to cung nhau ----------------------------------------*/ #include <fstream> #include <iostream> #include <math.h> using namespace std; // D A T A A N D V A R I A B L E S const int mn = 100001; const char * fn = "coprime.inp"; char p[mn]; // p[i] = 0=> i nguyen to int n; int imax, dmax; // P R O T O T Y P E S void Run(); void Duyet(); int Gcd(int, int); // I M P L E M E N T A T I O N int main() { Run(); cout << endl << " Fini "; cin.get(); return 0; } int Gcd(int a, int b) { int r; while (b) { r = a % b; a = b; b = r; } return a; } void Duyet() { int i, x, y, d; // d dem so cap nguyen to cung nhau imax = 0; dmax = 0; d = 0; ifstream f(fn); f >> n >> x; for (i = 2; i <= n; ++i) { f >> y; if (Gcd(x,y) == 1) ++d; else { if (d > dmax) { dmax = d; imax = i; }
  • 27. d = 0; } x = y; } f.close(); if (d > dmax) { dmax = d; imax = n; } ++dmax; imax = imax - dmax; } void Run() { Duyet(); cout << endl << imax << " " << dmax; } Bài toán Euler N 28 Trong ma trận vuông n X n, n lẻ xuất phát từ ô giữa ghi lần lượt các số từ 1..n2 theo đường xoắn ốc theo chiều kim dồng hồ. Với n = 5 tổng các phần tử trên 2 đường chéo là 101. Hãy tính tổng này với n = 1001. 21 22 23 24 25 20 7 8 9 10 19 6 1 2 11 18 5 4 3 12 17 16 15 14 13 Nhận xét Xét các số nằm ở bốn góc của hình vuôn cạnh n. Số ở góc trên phải là n2, các số còn lại lần lượt là n2−(n−1), n2−2(n−1) và n2−3(n−1). Tổng 4 số này là d(n) = n2 + n2−(n−1) + n2−2(n−1) + n2−3(n−1) = 4n2 − 6(n−1), n > 1 Với n = 1, d(1) = 1. Tổng các số trên hai đường chéo chính là s(n) = d(1) + d(3) +…+d(n). /* Name: Problem Euler 28
  • 28. Copyright: NXH 2010 Author: Nguyen Xuan Huy Date: 10/06/10 10:53 Description: 2 mau (den, trang) va 16 mau */ #include <iostream> #include <stdio.h> using namespace std; int Euler28(int n) { int e = 1; for (int i = 3; i <= n; i += 2) e += 4*i*i - 6*(i-1); return e; } main() { cout << endl << Euler28(1001); // 669171001 cout << endl << " Fini"; cin.get();return 0 ; } Bài toán Euler N 52 Số x = 125874, và 2x = 251748 được tạo bởi cùng chữ số theo trật tự khác nhau. Tìm số tự nhiên nhỏ nhất x sao cho 2x, 3x, 4x, 5x và 6x có các chữ số ddoooi một khác nhau và là những hoán vị của x. (142857) Có trên 5 số thỏa tính chất trên hay ko? Nếu ko đòi hỏi các chữ số khác nhau? Với các số nguyên dương 1..4294967295 (232 − 1) có 10 số 1. : 142857 2. : 1428570 3. : 1429857 4. : 14285700 5. : 14298570 6. : 14299857 7. : 142857000 8. : 142985700 9. : 142998570 10. : 142999857 Chương trình tính và ghi file 10 số /* Name: Problem Euler 28 Copyright: NXH 2010 Author: Nguyen Xuan Huy
  • 29. Date: 10/06/10 10:53 Description: 2 mau (den, trang) va 16 mau */ #include <iostream> #include <fstream> typedef uint32_t UI; const UI maxint = (UI)0xffffffff; using namespace std; char digits[10]; char mark[10]; void GetDigits(UI x, char dg[]) { for (int i = 0; i < 10; ++i) dg[i] = 0; do { ++dg[x%10]; x /= 10; } while (x); } bool SameDigits(UI x) { GetDigits(x,mark); for (int i = 0; i < 10; ++i) if (digits[i] != mark[i]) return false; return true; } bool Test(UI x) { GetDigits(x,digits); UI d = x; x += d; // 2x for (int i = 2; i <= 6; ++i, x += d) { if (!SameDigits(x)) return false; } return true; } UI Euler52(UI n) { bool b; for (UI x = n+1; x <= maxint; ++x) if (Test(x)) return x; return 0; } int Euler52m() { // co tren 10 so Euler52? int d; UI x = 0; ofstream f("Euler52.txt"); for (d = 0; (x = Euler52(x)) > 0;) { ++d; f << endl << d << ". : " << x;
  • 30. // if (d == 10) break; } f.close(); return d; } main() { int d = Euler52m(); cout << "nn Found: " << d; cout << endl << " Fini"; cin.get();return 0 ; } Các số nguyên tố Tìm được 340059 số, số cuối là 4870843 với lặp n < 500. Ý tưởng: nếu p[1..n] , c = p[n] là danh sách n số nguyên tố đầu tiên và x là một số lẻ trong khoảng c..c*c thì x là nguyên tố khi và chỉ khi x không có ước nguyên tố trong khoảng 3.. x. Xuất phát p = (2,3) − 2 số nguyên tố đầu tiên. n = 2 là số lượng các số nguyên tố hiện tìm được. Find: xét các số lẻ x trong khoảng từ pn + 2 đến pn2 − 2. Chú ý, do pn là số lẻ nên pn2 lẻ. Nếu x là ng tố thì thêm vào dãy. Để kiểm tra tính ng tố của x /* Name: Problem Euler 28 Copyright: NXH 2010 Author: Nguyen Xuan Huy Date: 10/06/10 10:53 Description: 2 mau (den, trang) va 16 mau */ #include <iostream> #include <math.h> typedef uint32_t UI; const UI maxn = 20000000; using namespace std; UI p[maxn]; UI n; bool IsPrime(UI x) { UI can = (UI) sqrt(x); for (UI i = 2; p[i] <= can; ++i) if (x % p[i] == 0) return false; return true; }
  • 31. // Tim tu n void Find() { UI n2 = p[n]*p[n] - 2; for (UI i = p[n] + 2; i <= n2; i += 2) if (IsPrime(i)) p[++n] = i; } void Run() { p[1] = 2; p[2] = 3; n = 2; while (n < 500) { cout << endl << " Find with n = " << n; Find(); cout << endl << " Tong cong " << n << " so nguyen to "; cout << endl << " So nguyen to lon nhat: " << p[n]; cin.get(); } } main() { Run(); cout << endl << " Fini"; cin.get(); return 0 ; } Problem Euler 1 If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000. /* Name: Problem Euler 01 Copyright: NXH 2010 Author: Nguyen Xuan Huy Date: 10/06/10 10:53 Description: If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000. */ #include <iostream> #include <stdio.h> const int mn = 1001;
  • 32. using namespace std; inline int Min(int a, int b) { return (a <= b) ? a : b; } int Euler01(int maxval) { int a[mn]; int n; int i3 = 0, i5 = 0; int s = 0; n = 0; a[0] = 1; while (a[n] < maxval) { while (3*a[i3] <= a[n]) ++i3; // 3*a[i3] > a[n] while (5*a[i5] <= a[n]) ++i5; // 5*a[i5] > a[n] a[++n] = Min(3*a[i3],5*a[i5]); } if (a[n] > maxval) --n; for (int i = 1; i <= n; ++i) s += a[i]; return s; } main() { cout << endl << Euler01(1000); // 3822 cout << endl << " Fini"; cin.get(); return 0 ; } Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... Find the sum of all the even-valued terms in the sequence which do not exceed four million. Thuật toán Ta có f1 = 1; f2 = 2 là số chẵn đầu tiên. Vì tổng lẻ + lẻ = chẵn và lẻ + chẵn = chẵn + lẻ = lẻ, nên sâu 2 số lẻ ta mới gặp 1 số chẵn. Từ đó suy ra các fi với i = 2 + 3k, k = 0, 1, … là các số chẵn. của số chẵn. Gọi fi là số chẵn, ta có: fi−1 là số lẻ sát trước fi. fi+1 = fi−1 + fi (lẻ),
  • 33. fi+2 = fi + fi+1 = fi + fi−1 + fi = 2fi + fi−1 (lẻ), fi+3 = f i+1 + fi+2 = fi−1 + fi + 2fi + fi−1 = 3fi + 2fi−1 = 2fi+2 − fi (chẵn) Ta sử dụng biến odd để lưu số lẻ và biến even lưu số chẵn sát sau odd. Ta có, Khởi trị: odd = 1; even = 2; Bước tiếp theo: odd = 2*even + odd; even = 2*odd – even; /* Name: Problem Euler 02 Copyright: NXH 2010 Author: Nguyen Xuan Huy Date: 10/06/10 10:53 Description: Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... Find the sum of all the even-valued terms in the sequence which do not exceed four million. */ #include <iostream> #include <stdio.h> using namespace std; int Euler02(int maxval) { int odd = 1, even = 2; int s = even; while (even < maxval) { odd = 2*even + odd; even = 2*odd - even; s += even; } if (even > maxval) s -= even; return s; } main() { cout << endl << Euler02(4000000); // 4613732 cout << endl << " Fini"; cin.get(); return 0 ; }
  • 34. Euler03 The prime factors of 13195 are 5, 7, 13 and 29. What is the largest prime factor of the number 600851475143 ? Khởi trị với danh sách chứa k = 2 số nguyên tố p[1] = 2; p[2] = 3. Hàm NextPrime(k) tìm số nguyên tố sát sau sô nguyên tố p[k]. Gọi hàm NextPrime nhiều lần đến khi thu được số nguyên tố p[k] > n . DuThe prime factors of 13195 are 5, 7, 13 and 29. What is the largest prime factor of the number 600851475143 ? (6857) /* Name: Euler03 Copyright: NXH 2010 Author: Nguyen Xuan Huy Date: 10/06/10 10:53 Description: The prime factors of 13195 are 5, 7, 13 and 29. What is the largest prime factor of the number 600851475143 ? */ #include <iostream> #include <stdio.h> #include <math.h> #include <time.h> using namespace std; typedef long double BI; // kieu gia nguyen BI p[100000]; BI x = 600851475143.0; // Test if y is a prime number bool IsPrime(BI y) { BI ys = floorl(sqrt(y)); for (int i = 1; p[i] <= ys; ++i) if (fmodl(y,p[i]) == 0.0) return false; return true; }
  • 35. // Given a list consequenced first k prime numbers // p[1..k] = (3,5,7,..p[k]), k > 1. // Find the next prime number (after p[k]) inline BI NextPrime(int k) { BI n; for (n = p[k] + 2; !IsPrime(n); n += 2); return n; } // Find the lagest prime factor of a given integer n BI Euler03(BI n) { BI sn; int i, imax, k = 2; BI m; // Initialize: p[1..2] = (2,3); k = 2: two first prime numbers (2 and 3). p[1] = 2; p[2] = 3; sn = floorl(sqrt(n)); cout << endl << " n = " << n << " sqrt(n) = " << sn; // Find consequenced prime numbers not greater than sqrt(n) while (p[k] < sn) { m = NextPrime(k); p[++k] = m; } cout << endl << " Found " << k << " prime number(s). " << endl; cout << endl << " The last one is : " << p[k]; // Find the last index imax in p[1..k] so p[imax] is a prime factor of n imax = 0; for (i = 1; i <= k; ++i) if (fmodl(n,p[i]) == 0.0) { imax = i; do { n /= p[i]; } while (fmodl(n,p[i]) == 0.0); } cout << "n Now n = " << n << " imax = " << imax; cout << " pmax = " << p[imax] << endl; // if imax = 0: n did not have any prime factors in p[1..k]
  • 36. // so n is a prime number. We return n if (imax == 0) return n; // n is a prime number return (n < p[imax]) ? p[imax] : n; } main() { time_t t1,t2; t1 = time(NULL); BI r = Euler03(x); // 6857 t2 = time(NULL); float d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; cout << "nn The result: " << r; cout << endl << " Fini"; cin.get(); return 0; } Euler04 A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 99. Find the largest palindrome made from the product of two 3-digit numbers. /* Name: Problem Euler 04 Copyright: NXH 2010 Author: Nguyen Xuan Huy Date: 10/06/10 10:53 Description: Euler04 A palindromic number reads the same both ways. The largest palindrome made from the product of two 2- digit numbers is 9009 = 91 99. Find the largest palindrome made from the product of two 3-digit numbers. */ #include <iostream> #include <stdio.h>;
  • 37. using namespace std; int Exp(int a, int x) { int z = 1; while (x) { if (x % 2 == 1) z *= a; a = a * a; x /= 2; } return z; } int Rev(int z) { int x = 0; while(z) { x = x * 10 + (z % 10); z /= 10; } return x; } void Euler04(int digitNumber, int & x, int & y) { int maxNum = Exp(10,digitNumber) - 1; int z; for (x = maxNum; x >= 0; --x) for (y = maxNum; y >= 0; --y){ z = x*y; if (z == Rev(z)) return; } } main() { int x, y; Euler04(3,x,y); cout << endl << x << " * " << y << " = " << (x * y); // x = 999, y = 91, x*y = 90909 cout << endl << " Fini"; cin.get(); return 0 ; } 1. 9 * 1 = 9 2. 99 * 91 = 9009 3. 995 * 583 = 580085 4. 9999 * 9901 = 99000099
  • 38. Euler69 Euler's Totient function, φ(n) [sometimes called the phi function], is used to determine the number of numbers less than n which are relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than nine and relatively prime to nine, φ(9)=6. n Relatively Prime Φ(n) n/φ(n) 2 1 1 2 3 1,2 2 1.5 4 1,3 2 2 5 1,2,3,4 4 1.25 6 1,5 2 3 7 1,2,3,4,5,6 6 1.1666... 8 1,3,5,7 4 2 9 1,2,4,5,7,8 6 1.5 10 1,3,7,9 4 2.5 It can be seen that n = 6 produces a maximum n/φ(n) for n 10. Find the value of n 1,000,000 for which n/φ(n) is a maximum. n = 100000 (một trăm ngàn); max phi(i)/i = 5.21354 đạt tại i = 30030; 1 sec. n = 1000000 (một triệu) ; max phi(i)/i = 5.53939 đạt tại i = 510510 dưới 1 sec. Nhận xét 1 Nếu n = p1m1 p2m2 … pkmk thì (n) = p1m1−1 p2m2−1 … pkmk−1.(p1−1)(p2−1)…(pk−1). Do đó , kí hiệu E(n) = n/(n), ta có E(n) = n/(n) = (p1p2…pk)/((p1−1)(p2−1)…(pk−1)) (*) Kí hiệu S(n) là tập các ước nguyên tố (khác nhau) của n, thí dụ, n = 60 thf S(n) = { 2, 3, 5 }. Công thức (*) cho ta biết, nếu hai số tự nhiên n và m có S(n) = S(m) thì E(n) = E(m). Thí dụ, n = 10i và m = 10j sẽ cho cùng giá trị E vì S(n) = S(m) = { 2, 5 }. Xét dãy liên tiếp các số nguyên tố pi 2, 3, 5,… Vì p < q suy ra pq−q < pq − p hay q(p−1) < p(q−1) tức q/(q−1) < p/(p−1). Vậy dãy pi/(pi-1) với i = 1, 2 giảm dần: pi/(pi-1) < pi+1/(pi+1-1). Vậy tích p1/(p1-1) * p2/(p2-1) * … * pk/(pk-1) đạt max khi tích p1p2…pk là số lớn nhất không vượt quá n. /* Name: Problem Euler 02 Copyright: NXH 2010 Author: Nguyen Xuan Huy Date: 10/06/10 10:53 Description: It can be seen that n=6 produces a maximum n/phi(n) for n <= 10. Find the value of n <=1,000,000 for which n/phi(n) is a maximum. */
  • 39. #include <iostream> #include <stdio.h> #include <math.h> #include <time.h> using namespace std; typedef uint32_t UI; UI p[10]; inline UI Min(UI a, UI b) { return ( a <= b) ? a : b; } bool IsPrime(UI y) { UI ys = (UI)(sqrt(y)); for (UI d = 1; p[d] <= ys; ++d) if (y % p[d] == 0) return false; return true; } inline UI NextPrime(int k) { for (k = p[k] + 2; !IsPrime(k); k += 2); return k; } float Euler69(UI n, UI & a) { int k = 2; UI m; UI b; p[1] = 2; p[2] = 3; a = p[1]*p[2]; b = (p[1]-1)*(p[2]-1); while (a < n) { m = NextPrime(k); p[++k] = m; a *= m; b *= (m-1); } if (a > n) { a /= m; b /= (m-1); --k; } cout << endl << " Found " << k << " prime numbers " << endl; for (int i = 1; i <= k; ++i) cout << p[i] << " "; return (float) a/b; } void Test() { time_t t1,t2; t1 = time(NULL); UI n; float r = Euler69(1000000, n); cout << endl << " result: n/phi(n) = " << r << " n = " << n; t2 = time(NULL); float d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; } main() { Test(); cout << (UI)(2*3*5*7*11*13*17*19);
  • 40. cout << endl << " Fini"; cin.get(); return 0; } Problem 5 30 November 2001 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder. What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20? Problem 6 14 December 2001 The sum of the squares of the first ten natural numbers is, 12+ 22 + ... + 102 = 385 The square of the sum of the first ten natural numbers is, (1 + 2 + ... + 10) 2 = 552= 3025 Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is 3025 − 385 = 2640. Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum. 2 phương án. Phương án 1. Tính theo 2 vòng lặp, O(n2). Phương án 2. O(n). Các công thức: n S (i )  i  k k  i 1 S(i) = i((i+1) + (i+2) + … + n) _(a1 + a2 + … + am)2 = a12 + a22 + … + an2 + a1a2 + a1a3 + … +a1an // Find the sum of all the primes below two million. #include <iostream> #include <math.h> using namespace std; const int mn = 200; int a[mn]; int Diff1(int n) { int s = 0;
  • 41. int i, j; for (i = 1; i < n; ++i) for(j = i+1; j <= n; ++j) s += i*j; return 2*s; } int Diff2(int n) { int s; int i; a[n] = n; for (i = n-1; i > 0; --i) a[i] = i+a[i+1]; s = a[2]; for(i = 2; i < n; ++i) s += i*a[i+1]; return 2*s; } main() { cout << endl << Diff1(100); cout << endl << Diff2(100); cout << endl << " Fini"; cin.get(); return 0; } Problem 7 28 December 2001 By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13. What is the 10001th prime number? Phương án 1. Sàng, sau đó duyệt để lấy số thứ n. Trong khoảng 1..200000 có 17984 số nguyên tố. Phương án 2. NextPrime sinh số nguyên tố sát sau. // Find the 10001-th prime number. #include <iostream> #include <math.h> using namespace std; const int mn1 = 200000; const int mn2 = 20000; char p[mn1+1]; int a[mn2]; void Eratosthenes(int n) { int i, j, n2, c = (int)sqrt(n); memset(p,0,sizeof(p)); for (i = 2; i < c; ++i) if (p[i] == 0) { n2 = n/i; for (j = i; j <= n2; ++j) p[i*j] = 1; } } int PrimeN1(int n) { // phuong an 1 int d = 0;
  • 42. Eratosthenes(mn1); for (int i = 2; i < mn1; ++i) if (p[i] == 0) { ++d; if (d == n) return i; } } bool IsPrime(int m) { int sq = (int)sqrt(m); int i; for (i = 1; a[i] <= sq; ++i) if (m % a[i] == 0) return false; return true; } // Biet k so nguyen to. // Yim so thu k+1 int NextPrime(int k) { int m = a[k]; do { m += 2; } while (!IsPrime(m)); return m; } int PrimeN2(int n) { a[1] = 2; a[2] = 3; // Khoi tri voi 2 so ng to dau tien int k = 2, m; for (int i = 1; i <= n; ++i) { m = NextPrime(k); a[++k] = m; } return a[n]; } main() { cout << endl << PrimeN1(10001); // 104743 cout << endl << PrimeN2(10001); // 104743 cout << endl << " Fini"; cin.get(); return 0; } Problem 8 11 January 2002 Find the greatest product of five consecutive digits in the 1000-digit number. 73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 85861560789112949495459501737958331952853208805511 12540698747158523863050715693290963295227443043557 66896648950445244523161731856403098711121722383113 62229893423380308135336276614282806444486645238749 30358907296290491560440772390713810515859307960866
  • 43. 70172427121883998797908792274921901699720888093776 65727333001053367881220235421809751254540594752243 52584907711670556013604839586446706324415722155397 53697817977846174064955149290862569321978468622482 83972241375657056057490261407972968652414535100474 82166370484403199890008895243450658541227588666881 16427171479924442928230863465674813919123162824586 17866458359124566529476545682848912883142607690042 24219022671055626321111109370544217506941658960408 07198403850962455444362981230987879927244284909188 84580156166097919133875499200524063689912560717606 05886116467109405077541002256983155200055935729725 71636269561882670428252483600823257530420752963450 Biến thể 1. Tính tổng max của k phần tử liên tiếp. Cửa sổ trượt. // Find the greatest sum of k consecutive digits // in the n-digit number. #include <iostream> #include <fstream> #include <stdio.h> #include <math.h> #include <time.h> using namespace std; const char * fn = "Euler8.txt"; const int mn = 5000; char a[mn]; int n; // so luong phan tu int k; // chieu dai doan inline bool IsDigit(char c) { return (c >= '0' && c <= '9'); } void ReadData() { ifstream f(fn); int i = 0; char c; while (!f.eof()) { f >> c; if (IsDigit(c)) a[++i] = c-'0'; } f.close(); for (i = 1; i <= n; ++i) cout << (int)a[i]; } inline int Max(int a, int b) { return (a > b) ? a : b; } int Sum() { int s = 0, smax; int i, j; // Sum of the first k elements
  • 44. for (i = 1; i <= k; ++i) s += a[i]; smax = s; i = 1; for (j = k + 1; j <= n; ++j,++i) { s = s + a[j] - a[i]; smax = Max(smax,s); } return smax; } main() { time_t t1,t2; t1 = time(NULL); ReadData(); cout << endl << endl << Sum() << endl; t2 = time(NULL); float d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; cout << endl << " Fini"; cin.get(); return 0; } Biến thể 2. Tích max của k phần tử liên tiếp. Cửa sổ trượt. // Find the greatest product of k consecutive digits // in the n-digit number. #include <iostream> #include <fstream> #include <stdio.h> #include <math.h> #include <time.h> using namespace std; const char * fn = "Euler8.txt"; const int mn = 5000; char a[mn]; int n; // so luong phan tu int k; // chieu dai doan inline bool IsDigit(char c) { return (c >= '0' && c <= '9'); } void ReadData() { ifstream f(fn); f >> n >> k; cout << endl << " n = " << n << " k = " << k << endl; int i = 0; char c; while (!f.eof()) { f >> c; if (IsDigit(c)) a[++i] = c-'0'; } f.close(); for (i = 1; i <= n; ++i) cout << (int)a[i];
  • 45. } inline int Max(int a, int b) { return (a > b) ? a : b; } int Product() { int s , smax = 0 ; int i, j, d; s = 1; for (j = 1; j <= k; ++j) s *= a[j]; smax = Max(smax,s); i = 1; for (j = k+1; j <= n; ++j, ++i) { if (a[i] > 0) s /= a[i]; else { // a[i] = 0 s = 1; for (d = i+1; d < j; ++d) s *= a[d]; } s *= a[j]; smax = Max(s,smax); } return smax; } main() { time_t t1,t2; t1 = time(NULL); ReadData(); cout << endl << endl << Product() << endl; t2 = time(NULL); float d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; cout << endl << " Fini"; cin.get(); return 0; } Cải tiến biến thể 2. loại trừ nhưnTích max gx a[j] = 0. Ch][ng trinhf tinhs car 2 ph][ng ans vaf xacs ddinhj vị trì jmax (phần tử cuối dãy đạt tích max). // Find the greatest sum of k consecutive // digits in the n-digit number. #include <iostream> #include <fstream> #include <stdio.h> #include <math.h> #include <time.h> using namespace std; const char * fn = "Euler8.txt"; const int mn = 5000;
  • 46. char a[mn]; int n; // so luong phan tu int k; // chieu dai doan int jmax; inline bool IsDigit(char c) { return (c >= '0' && c <= '9'); } void ReadData() { ifstream f(fn); f >> n >> k; cout << endl << " n = " << n << " k = " << k << endl; int i = 0; char c; while (!f.eof()) { f >> c; if (IsDigit(c)) a[++i] = c-'0'; } f.close(); for (i = 1; i <= n; ++i) cout << (int)a[i]; } inline int Max(int a, int b) { return (a > b) ? a : b; } int Product1() { int s , smax = 0 ; int i, j, d; s = 1; for (j = 1; j <= k; ++j) s *= a[j]; if (smax < s) { smax = s; jmax = k; } i = 1; for (j = k+1; j <= n; ++j, ++i) { if (a[i] > 0) s /= a[i]; else { // a[i] = 0 s = 1; for (d = i+1; d < j; ++d) s *= a[d]; } s *= a[j]; if (smax < s) { smax = s; jmax = j; } } return smax; } int Product2() { int s , smax = 0 ; int i = 0, j, d; s = 1; d = 0; for (j = 1; j <= n; ++j) { if (a[j] == 0) { i = j; s = 1; d = 0; } else { // s[j] > 0 s *= a[j]; ++d; if (d == k) { if (smax < s) { smax = s; jmax = j; } ++i; s /= a[i]; --d; }
  • 47. } } return smax; } main() { float d; time_t t1,t2; ReadData(); t1 = time(NULL); cout << endl << endl << Product1() << " jmax = " << jmax << endl; t2 = time(NULL); d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; t1 = time(NULL); cout << endl << endl << Product2() << " jmax = " << jmax << endl; t2 = time(NULL); d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; cout << endl << " Fini"; cin.get(); return 0; } Problem 9 25 January 2002 A Pythagorean triplet is a set of three positive integes (a, b, c), for which, a2 + b2 = c2. For example, 32 + 42 = 52 ( 9 + 16 = 25). There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product abc. Nhận xét 1. Trong bộ ba Pithagor không có 2 số bằng nhau. Nếu a = b thì c  2 a là số vô tỷ, không phải số nguyên. Nếu a = c thì b = 0, nếu b = c thì a = 0. Từ nhận xét này ta có thể quy định 0 < a < b < c. 2. Nếu tổng a+b+c = k thì nhận xét 1 cho ta 1 ≤ a ≤ m, a+1 ≤ b ≤ 2m, c = k−a−b; m = k/3. /*−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− There exists exactly one Pythagorean triplet for which a + b + c = 1000.
  • 48. Find the product abc. −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−-*/ #include <iostream> #include <stdio.h> using namespace std; int Pit() { const int k = 1000; int a, b, c, c2, a2, ab2, lim, lim2; lim = k/3; lim2 = 2*lim; for (a = 1; a <= lim; ++a) { a2 = a*a; for (b = a+1; b <= lim2; ++b) { ab2 = a2 + b*b; c = k - a - b; c2 = c*c; if (c*c == ab2) return (a * b * c); } } return 0; } main() { cout << endl << Pit(); cout << endl << " Fini"; cin.get(); return 0; } Problem 10 08 February 2002 The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two million. // Find the sum of all the primes below two million. #include <iostream> #include <math.h> using namespace std; const int mn = 2000000; char a[mn]; void Eratosthenes(int n) { int i, j, n2, c = (int)sqrt(n); memset(a,0,sizeof(a)); for (i = 2; i < c; ++i) if (a[i] == 0) { n2 = n/i; for (j = i; j <= n2; ++j) a[i*j] = 1; } } unsigned int Sum(int n) {
  • 49. unsigned int s = 0; Eratosthenes(n); for (int i = 2; i <= n; ++i) if (a[i] == 0) s += i; return s; } main() { int n = mn-1; cout << Sum(n); // 1179908154 cout << endl << " Fini"; cin.get(); return 0; } Problem 11 22 February 2002 In the 20 20 grid below, four numbers along a diagonal line have been marked in red. 08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48 The product of these numbers is 26 63 78 14 = 1788696. What is the greatest product of four adjacent numbers in any direction (up, down, left, right, or diagonally) in the 20 20 grid?
  • 50. Problem 14 05 April 2002 The following iterative sequence is defined for the set of positive integers: n → n/2 (n is even) n → 3n + 1 (n is odd) Using the rule above and starting with 13, we generate the following sequence: 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1 It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1. Which starting number, under one million, produces the longest chain? NOTE: Once the chain starts the terms are allowed to go above one million. Đề: Collatz Problem (Bài toán 3n+1). Với mỗi số tự nhiên n ta thực hiện các thao tác sau để sinh ra dãy số: Nếu n lẻ thì thay n bằng 3n+1; ngược lại, nếu n chẵn thì thay nn bằng n/2. Xác định số n  1000000 (1 triệu) để dãy số sinh ra là dài nhất. Đáp số: n = 837799 sinh dãy dài 525 số. Chú ý rằn có thể lúc đầu giá trị n nhỏ (dưới 1 triệu), nhưng sau một số bước giá trị này có thể tăng lên đến vài tỷ. Thí dụ, 837799 → 2513398 → 1256699→ 3770098→ 1885049→ 5655148→ 2827574→ 1413787→ 4241362 → 2120681→ 6362044→ 3181022→ 1590511→ 4771534→ 2385767→ 7157302→ 3578651 → 10735954→ 5367977→ 16103932→ 8051966→ 4025983→ 12077950→ 6038975→ 18116926 → 9058463→ 27175390→ 13587695→ 40763086→ 20381543→ 61144630→ 30572315→ 91716946 → 45858473→ 137575420→ 68787710→ 34393855→ 103181566→ 51590783→ 154772350 → 77386175→ 232158526→ 116079263→ 348237790→ 174118895→ 522356686→ 261178343 → 783535030→ 391767515→ 1175302546. Thuật toán Ta tạm gọi dãy sinh ra là dãy 3n+1 Nhận xét. 1. Dãy 3n+1 là dãy không lặp, do đó sinh xuôi và sinh ngược đều các dãy không lặp Phương án 1: vét Ta duyệt với mỗi n = 2..1000000 xác định chiều dài lớn nhất của dãy thu được theo quy tắc ra trong đề bài. Để ý rằng khi n lẻ ta tính n’ = 3n+1. Ta thấy n’ chẵn và n’ > n trong khi ta cần dãy giảm đến 1. Vì vậy, khi n lẻ ta tính luôn 2 bước n’ = 3n+1 và n’’ = n’/2 = 2n/2 + (n+1)/2 = n + (n+1)/2. Với thời gian không quá 1’’ bạn có thể tìm ra đáp số n = 837799 đạt chiều dài max = 525. Để ý rằng nếu n chẵn thì s(n) = s(n/2) + 1 n lẻ thì s(n) = s(n+(n+1)/2) + 2; Nên chạy trong Turbo Pascal vì Free Pascal không ổn định. Thay n/2 bằng n >> 1 sẽ nhanh hơn là dùng phép chia. (*------------------------------------------------ Problem Euler 14 Ver. 1
  • 51. Find an integer n under one million that generates the longest chain by the following rule if n is even then replace n by n/2 else replace n by 3n + 1 --------------------------------------------------*) const mn = 1000000; function Try(n: longint): longint; var d: longint; begin d := 1; while (n > 1) do if Odd(n) then begin d := d+2; n := n+((n+1) shr 1); end else begin inc(d); n := n shr 1; end; Try := d; end; procedure Find(mn: longint); var i, d, dmax, imax: longint; begin dmax := 1; imax := 1; for i := 2 to mn-1 do begin d := Try(i); if (d > dmax) then begin dmax := d; imax := i; end; end; writeln(dmax,' ', imax); end; BEGIN Find(mn); readln; END. /*------------------------------------------------ Problem Euler 14 Ver. 1 Find an integer n under one million that generates the longest chain by the following rule if n is even then replace n by n/2 else replace n by 3n + 1 --------------------------------------------------*/
  • 52. #include <iostream> #include <fstream> #define Odd(n) (((n) & one) == one) using namespace std; typedef unsigned int UI; const UI mn = 1000000; const UI one = (UI)1; int Try(UI n) { int d = 1; while (n > 1) { if (Odd(n)) { d += 2; n = n+((n+1)>>1); } else { d++; n >>= 1; } } return d; } void Find(int mn) { UI i; int d, dmax = 1, imax = 1 ; for (i = 2; i < mn; ++i) { d = Try(i); if (d > dmax) { dmax = d; imax = i; } } cout << endl << imax << " " << dmax; } main() { float d; time_t t1,t2; t1 = time(NULL); Find(mn); t2 = time(NULL); d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; cout << endl << " Fini"; cin.get(); return 0; } Phương án 2. Dùng quy hoạch động hạn chế. Kí hiệu s(n) là chiều dài dãy 3n+1. Ta có ngay: s(n) = s(n/2) + 1, nếu n chẵn s(n) = s(n+(n+1)/2) + 2, nếu n lẻ Ta sử dụng mảng a gồm mn = 1 triệu phần tử, a[i] = s(i) và sẽ lặp cho đến khi n < mn để đánh dấu
  • 53. /*-------------------------------------------------------- Problem Euler 14 Ver. 2 Find an integer n under one million that generates the longest chain by the following rule if n is even then replace n by n/2 else replace n by 3n + 1 --------------------------------------------------------*/ #include <iostream> #include <fstream> #define Odd(n) (n & 1) == 1 #define Even(n) !Odd(n) using namespace std; typedef unsigned int UI; const UI mn = 1000000; UI a[mn]; int Try(UI n) { int d = 1, v = n; if (Even(v)) { a[n] = a[v/2] + 1; return a[n]; } while (v > 1) { if (Odd(v)) { d += 2; v += ((v+1)>>1); } else { d++; v >>= 1; } if (v < n) { if (a[v] > 0) { a[n] = a[v] + d - 1; return a[n]; } } } a[n] = d; return a[n]; } int Test(UI n) { int d = 1; while (n > 1) { if (Odd(n)) { d += 2; n += ((n+1)>>1); } else { d++; n >>= 1; } } return d; } void Find(int mn) { UI i; int d, dmax = 1, imax = 1; memset(a,0,sizeof(a)); a[1] = 1;
  • 54. for (i = 2; i < mn; ++i) { d = Try(i); if (d > dmax) { dmax = d; imax = i; } } cout << endl << imax << " " << dmax; } main() { float d; time_t t1,t2; t1 = time(NULL); Find(mn); t2 = time(NULL); d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; cout << endl << " Fini"; cin.get(); return 0; } Phương án 3. FromOne: Sinh dãy ngược từ 1. Đánh dấu độ dài của các phần tử được sinh mới trong dãy. Sau đó duyệt lại mảng, xét những phần tử chưa đánh dấu ta sinh xuôi dãy và cũng đánh dấu các phần tử được sinh trong dãy. /*------------------------------------------------ Problem Euler 14 Ver. 3 Find an integer n under one million that generates the longest chain by the following rule if n is even then replace n by n/2 else replace n by 3n + 1 --------------------------------------------------*/ #include <iostream> #include <fstream> #define Odd(n) (n & 1) == 1 #define Even(n) !Odd(n) using namespace std; typedef unsigned int UI; const UI mn = 1000000; const UI mn2 = mn+2; UI len[mn]; UI q[mn]; UI v,r; // con tro ngan xep st UI lenmax, imax; void FromOne() { UI x, y; UI s;
  • 55. memset(len,sizeof(len),0); v = r = 0; len[1] = 1; q[++v] = 1; do { x = q[++r]; s = len[x]; y = 2*x; if (y < mn) if (!len[y]) { len[y] = s+1; q[++v] = y; } if (Even(x)&&((x-1)%3==0)) { y = (x-1)/3; if (!len[y]) { len[y] = s+1; q[++v] = y; } } } while (v != r); lenmax = s ; imax = x; } void ToOne(UI n) { int d = 1; int x = n; int s, i; q[d] = n; if (Even(x)) { len[n] = len[x/2] + 1; if (lenmax < len[n]) { lenmax = len[n]; imax = n; } return; } while (x > 1) { if (Odd(x)) { q[++d] = 3*x+1; x = q[d]/2; q[++d] = x; } else { x >>= 1; q[d++] = x; } if (x < mn) if (len[x]) { len[n] = len[x] + d - 1; if (lenmax < len[n]) { lenmax = len[n]; imax = n; } s = len[n]+1; for (i = 2; i <= d; ++i) if (q[i] < mn) len[q[i]] = s-i; return; } } } void Find() { UI i; FromOne(); for (i = 2; i < mn; ++i) if (len[i] == 0) ToOne(i); cout << endl << " lenmax = " << lenmax << " imax = " << imax; }
  • 56. main() { float d; time_t t1,t2; t1 = time(NULL); Find(); t2 = time(NULL); d = difftime(t2,t1);// sec. cout << endl << " Time: " << d << " sec. " << endl; cout << endl << " Fini"; cin.get(); return 0; } Problem 15 19 April 2002 Starting in the top left corner of a 2 2 grid, there are 6 routes (without backtracking) to the bottom right corner. How many routes are there through a 20 20 grid? Thuật toán Ta giải bài toán tổng quát với lưới đơn vị gồm chiều dài n, rộng m. Trên lưới vuông này ta đánh nhãn các cạnh đơn vị nằm ngang là D, dọc là R. Mọi đường đi không quay lui từ góc trên trái đến góc dưới phải đều phải qua đúng n cạnh ngang và m cạnh dọc. Bài toán quy về dạng tổ hợp như sau. Có thể xây dựng bao nhiêu xâu kí tự độ dài n+m từ hai kí tự D và R trong đó có đúng n kí tự D và m kí tự R. Như vậy ta chỉ cần chọn đúng n vị trí trong số các vị trí từ 1..(n+m) để điền kí tự D. Các vị trí còn lại ta điền kí tự R.Vậy đáp số sẽ là tổ hợp chặp 2 của 2n Cnn+m = (n+m)!/(n!m!) = ((n+1)(n+2)...(n+m))/m! (*) Với n = m = 20 ta phải tính toán với các số lớn của tử và mẫu số trong công thức (*). Ta chọn phương pháp tính khác, cụ thể là ta dùng tam giác Pascal. Tam giác Pascal có những tính chất sau : (T1) Dòng thứ n có n+1 phần tử mã số từ 0 đến n. (T2) Phần tử thứ k trên dòng n, Ckn = tổ hợp chặp k của n phần tử = số hạng thứ k trong dạng khai triển của nhị thức (a+b)n = tổng của số hạng thứ k và k-1 trên dòng sát trước, dòng n-1 (Ckn= Ckn-1 + Ck-1n-1). (T3) Mỗi dòng là một dãy số được bố trí đối xứng nhau. Tính chất này cho phép ta chỉ cẫn xác định các giá trị trên một nửa dòng. 2 11 3 121 4 1331 5 14641 6 1 5 10 10 5 1
  • 57. 7 1 6 15 20 15 6 1 // Problem Euler 15 #include <iostream> #define Even(c) ((c)&1)==0 #define Odd(c) !Even(c) const int maxN = 21; int a[maxN]; using namespace std; int Pas(int n, int m) { int nm = n+m, i, i2, j; a[0] = a[1] = 1; for (i = 2; i <= nm; ++i) { i2 = i/2; a[i2] = (Odd(i)) ? a[i2]+a[i2-1] : 2*a[i2-1]; for (j = i2-1; j > 0; --j) a[j] = a[j]+a[j-1]; } return (n < m) ? a[n] : a[m]; } main() { cout << endl << Pas(2,2); // 407575348 cout << endl << " Fini"; cin.get(); return 0; } Problem 16 03 May 2002 215 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26. What is the sum of the digits of the number 21000? Thuật toán Tính trực tiếp. Khởi trị a = 2; lặp n lần a = a * 2; // Problem Euler 16 // Tính tong cac chu so cua so 2^1000 #include <iostream> const int maxN = 500; int a[maxN]; int len; using namespace std; // 2^1 void Init() { memset(a,0,sizeof(a)); a[0] = 2; len = 1; }
  • 58. // a = 2*a void Mult2() { int c = 0; for (int i = 0; i < len; ++i) { c = (a[i]<<1) + c; a[i] = c % 10; c /= 10; } if (c) a[len++] = c; } int Sum(int n) { int i,s = 0; Init(); for (i = 2; i <= n; ++i) Mult2(); for (i = 0; i < len ; ++i) s += a[i]; return s; } main() { cout << endl << Sum(1000) << " len = " << len; // 1366, 302 chu so cout << endl << " Fini"; cin.get(); return 0; } Viết dãy số Nếu sử dụng các tấm bìa, trên mỗi tấm ghi 1 chữ số thì đê ghi liên tiếp các số từ 1 đến 10 ta cần 21 tấm. 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 Cần bao nhiêu bìa để viết các số từ 1 đến 1000? Problem 18 31 May 2002 By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is 23. 3 7 4 2 4 6 8 5 9 3 That is, 3 + 7 + 4 + 9 = 23.
  • 59. Find the maximum total from top to bottom of the triangle below: 75 95 64 17 47 82 18 35 87 10 20 04 82 47 65 19 01 23 75 03 34 88 02 77 73 07 63 67 99 65 04 28 06 16 70 92 41 41 26 56 83 40 80 70 33 41 48 72 33 47 32 37 16 94 29 53 71 44 65 25 43 91 52 97 51 14 70 11 33 28 77 73 17 78 39 68 17 57 91 71 52 38 17 14 91 43 58 50 27 29 48 63 66 04 68 89 53 67 30 73 16 69 87 40 31 04 62 98 27 23 09 70 98 73 93 38 53 60 04 23 NOTE: As there are only 16384 routes, it is possible to solve this problem by trying every route. However, Problem 67, is the same challenge with a triangle containing one- hundred rows; it cannot be solved by brute force, and requires a clever method!) Thuật toán Đọc dần dữ liệu tính trọng số tại mỗi đỉnh k. Đỉnh trên cạnh (nằm ngoài cùng ở 2 đầu) chỉ có 1 cha. Đỉnh trong có 2 cha. Nếu k là đỉnh đầu tiên của dòng i thì cha của k là k-i+1. Nếu k là đỉnh cuối cùng của dòng i thì cha của k là k-i. Nếu đỉnh k nằm giữa dòng i thì k có 2 cha là k-i và k-i+1. // Problem Euler 16 // Tính tong cac chu so cua so 2^1000 #include <iostream> #include <fstream> #define Max(a,b) ((a > b) ? a : b) const int maxN = 5000; const char * fn = "E18.inp"; int a[maxN]; using namespace std; int MaxTotal(){ int n, i, j, x, k = 1, maxval = 0; ifstream f(fn); f >> n; f >> x; a[k] = x; for (i = 2; i <= n; ++i) { f >> x; ++k; a[k] = a[k-i+1]+x; for (j = 2; j < i; ++j) { f >> x; ++k; a[k] = Max(a[k-i],a[k-i+1]) + x; }
  • 60. f >> x; ++k; a[k] = a[k-i] + x; } f.close(); for (j = 0; j < n; ++j) maxval = Max(maxval,a[k-j]); return maxval; } main() { cout << endl << MaxTotal(); // 1074 cout << endl << " Fini"; cin.get(); return 0; } Problem 19 14 June 2002 You are given the following information, but you may prefer to do some research for yourself. 1 Jan 1900 was a Monday. Thirty days has September, April, June and November. All the rest have thirty-one, Saving February alone, Which has twenty-eight, rain or shine. And on leap years, twenty-nine. A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400. How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)? Problem 20 21 June 2002 n! means n (n 1) ... 3 2 1. Find the sum of the digits in the number 100! n n! sum 1 1 1 2 2 2 3 6 6 4 24 6 5 120 3 6 720 9 7 5040 9 Thuật toán Làm trực tiếp. Biểu diễn số lớn. // E20 Tong các chu so cua n! #include <iostream>
  • 61. using namespace std; const int mn = 200; int a[mn]; int len; void Print(char *msg) { cout << msg; for (int i = len-1; i >= 0; --i) cout << a[i]; cout << "(" << len <<")"; } void Init() { len = 1; a[0] = 1; } // a = a * x void Mult(int x) { int c = 0, i; for (i = 0; i < len; ++i) { c = a[i]*x + c; a[i] = c % 10; c /= 10; } while (c) { a[len++] = c % 10; c /= 10; } } int Sum(int n) { int i; Init(); for (i = 2; i <= n; ++i) Mult(i); int s = 0; for (i = 0; i < len; ++i) s += a[i]; return s; } main() { cout << Sum(100); // 648 cout << endl << " Fini !"; cin.get(); } Problem 21. Số thân thiện 05 July 2002 Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n). If d(a) = b and d(b) = a, where a b, then a and b are an amicable pair and each of a and b are called amicable numbers. For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220. Evaluate the sum of all the amicable numbers under 10000. Thuật toán
  • 62. Nhận xét 5 cặp số thân thiện trong khoảng 1..10000 220 - 284 1184 - 1210 2620 - 2924 5020 - 5564 6232 - 6368 Tổng: 31626. Dùng mảng a đánh dấu các phần tử đã duyệt, giảm số lần xử lý. // E20 Amicable Numbers So than thien #include <iostream> #include <math.h> using namespace std; const int mn = 10000; int a[mn+1]; // Tong cac uoc < n cua n int Su(int n) { int u, c = (int)sqrt(n), s; if (n == 1) return 0; s = 1; if (c*c == n) { s += c; --c; } for (u = 2; u <= c; ++u) if (n % u == 0) s += u + (n/u); return s; } int Ami(int n) { int i, d, j, s = 0, v = 0; memset(a,0, sizeof(a)); for (i = 2; i <= n ; ++i) if (a[i] == 0) { ++v; a[i] = 1; d = Su(i); if (d <= n) if (a[d] == 0) { a[d] = 1; j = Su(d); if (j == i && i != d) { cout << endl << i << " " << d; s += i + d; } } } cout << endl << " So lan duyet: " << v << endl; return s; } main() { cout << endl << Ami(mn); // 31626 cout << endl << " Fini !"; cin.get(); }
  • 63. Problem 22 19 July 2002 Using names.txt (right click and 'Save Link/Target As...'), a 46K text file containing over five-thousand first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, multiply this value by its alphabetical position in the list to obtain a name score. For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would obtain a score of 938 53 = 49714. What is the total of all the name scores in the file? Problem 23. Số hoàn thiện 02 August 2002 A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number. A number n is called deficient (hụt, thiếu) if the sum of its proper divisors is less than n and it is called abundant (trội, dôi, dồi dào) if this sum exceeds n. As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit. Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers. Thuật toán Chương trình khảo sát các loại số. Scan: Duyệt các số từ 1 đến 28123. Gọi Su(i) là tổng các ước thực sự của số nguyên dương i. Đánh dấu mỗi số i theo các nhãn: a[i] = 0: nếu i là số hoàn thiện, tức là Su(i) = i; a[i] = 1: nếu i là số hụt, tức là Su(i) < i; a[i] = 2: nếu i là số dôi, tức là Su(i) > i; Đồng thời nhặt riêng các số dôi đưa vào mảng b. Gọi k là biến đếm các số dôi, ta có mảng b[1..k] chứa toàn số dôi. Asum: Duyệt các cặp số dôi b[i], b[j], 1  i, j  k. Đánh dấu các số là tổng của b[i] và b[j]. /*------------------------------------------- E23 perfect Numbers So hoan thien Su(n) = tong cac uoc that su cua n - Su(n) = n: So hoan thien (perfect number) - Su(n) < n: So hut (deficient) - Su(n) > n: So doi (abundant) So tong doi la so la tong cua 2 so doi. Moi so tu nhien lon hon 28123 deu la so tong doi. Tinh tong cua cac so khong tong doi. 6965 so abudant. Tim duoc 1456 so --------------------------------------------*/ #include <iostream> #include <math.h> using namespace std; #define abundant 2 #define perfect 0
  • 64. #define deficient 1 #define asum 4 const int mn = 28124; int a[mn]; int b[mn]; // danh sach cac so abudant int k; // Tong cac uoc < n cua n int Su(int n) { int u, c = (int)sqrt(n), s; if (n == 1) return 0; s = 1; if (c*c == n) { s += c; --c; } for (u = 2; u <= c; ++u) if (n % u == 0) s += u + (n/u); return s; } int Scan() { int i, d, k = 0; memset(a,0,sizeof(a)); for (i = 1; i < mn; ++i) { d = Su(i); if (d == i) a[i] = perfect; else if (d < i) a[i] = deficient; else { a[i] = abundant; b[++k] = i; } } return k; } void ShowAbudant() { int i; cout << endl << k << " so abudant: " << endl; cout << endl; cin.get(); for (i = 1; i <= k; ++i) cout << b[i] << " "; } void ShowAll() { int i; cout << endl << " Danh sach tong the: " << endl; for (i = 1; i < mn; ++i) { cout << i << ":"; switch(a[i]) { case perfect: cout << "p "; break; case deficient: cout << "d "; break; case abundant: cout << "a "; break; } } } // Cac so la tong cua 2 abudant int Asum() { int i, j, s; for (i = 1; i <= k; ++i) for (j = 1; j <= k; ++j) { s = b[i]+b[j]; if (s < mn) a[s] = asum; }
  • 65. s = 0; for (i = 1; i < mn; ++i) if (a[i] != asum) { s += i; } return s; } void TestAbudant() { int i; for (i = 1; i <= k; ++i) { cout << endl << b[i] << " " << Su(b[i]); cin.get(); } } void Run() { int i; k = Scan(); cout << endl << endl << " Total: " << Asum(); // 4179871 } main() { Run(); cout << endl << " Fini !"; cin.get(); } Problem 25 30 August 2002 The Fibonacci sequence is defined by the recurrence relation: Fn = Fn 1 + Fn 2, where F1 = 1 and F2 = 1. Hence the first 12 terms will be: F1=1, F2=1, F3=2, F4=3, F5=5, F6=8, F7=13, F8=21, F9=34, F10=55, F11=89, F12 = 144. The 12th term, F12, is the first term to contain three digits. What is the first term in the Fibonacci sequence to contain 1000 digits? Tính trực tiếp Cài đặt phép cộng và phép gán các số lớn. Tính gần đúng: 1 5 Kí hiệu  . Gọi fn là số Fibonacci thứ n (tính từ 1). Dùng qui nạp toán học có thể chưng minh 2 được n-2 < fn  n-1. Ta biết số chữ số của số nguyên dương n là int(lg(n))+1, trong đó int(x) là hàm lấy phần nguyên của số thực x, lg là hàm logarithm cơ số 10 (trong C viết là log10). Vì hàm lg là đồng biến nên bất phương trình kép trên cho ta (n-2) + 1 < lg(fn) + 1  (n-1) +1. /*------------------------------------------- E25 Find the first term of Fibonacci sequence to contain 1000 digits --------------------------------------------*/ #include <iostream>
  • 66. #include <math.h> using namespace std; #define Max(a,b) ((a > b) ? a : b) #define Min(a,b) ((a > b) ? b : a) const int mn = 1000; int lena, lenb, lenc; int a[mn], b[mn], c[mn]; // c = a + b; void Plus() { memset(c,0,sizeof(c)); lenc = Max(lena, lenb); int i, v = 0; for (i = 0; i < lenc; ++i) { v = v + a[i] + b[i]; c[i] = v % 10; v /= 10; } while (v) { c[lenc++] = v % 10; v /= 10;} } int Fib(int n) { memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); a[0] = b[0] = 1; lena = lenb = 1; lenc = 0; int i = 2; while (lenc < n) { Plus(); memcpy(a, b, sizeof(b)); lena = lenb; memcpy(b, c, sizeof(c)); lenb = lenc; ++i; } return i; } // f(n) <= x^(n-1); m = lg(f{n)) = (n-1)lg(x) // m/lg(x)+1 main() { cout << endl << endl ; int n = 1000; int m; float x = (sqrt(5)+1)/2; float y = log10(x); cout << endl << (int)pow(x,n-2) << " " << (int)pow(x,n-1); cout << endl << (int(n/y) + 1); cout << endl << Fib(n); // 4785 cout << endl << lena << " " << lenb << " " << lenc; cout << endl << " Fini !";
  • 67. cin.get(); } Problem 26 13 September 2002 A unit fraction contains 1 in the numerator. The decimal representation of the unit fractions with denominators 2 to 10 are given: 1 = 0.5 /2 1 = 0.(3) /3 1 = 0.25 /4 1 = 0.2 /5 1 = 0.1(6) /6 1 = 0.(142857) /7 1 = 0.125 /8 1 = 0.(1) /9 1 = 0.1 /10 Where 0.1(6) means 0.166666..., and has a 1-digit recurring cycle. It can be seen that 1/7 has a 6-digit recurring cycle. Find the value of d 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part. Fib Dãy số nguyên đựơc gọi là tựa Fibonacci ...,f-2, f-1, f0, f1, f2, ... có tính chất fi = fi-1+fi-2. Biết 5 giá trị i, fi, j, fj, n, i ≠ j. Viết hàm Fib(i, fi, j, fj, n) tính fn ? Thí dụ Fib(0, 7, -5, -1, 3) = 29. Ý nghĩa: Biết f0 = 7, f-5 = -1 ta tính được f3 = 29 -1 2 1 3 4 7 11 18 29 Thuật toán Giả sử i < j. Nếu n = i ta cho kết quả fn = fi, Nếu n = j ta cho kết quả fn = fj, Nếu n ≠ i ta xử lý như sau. Gọi ci, i = 1, 2, … là dãy Fibonacci chuẩn, tức là dãy c1=1, c2=1, c3 = c2+c1 = 2, ,..., ci = ci-1+ci-2 ; i = 3, 4, …. Ta tìm cách biểu thị dãy tựa Fibonacci qua dãy Fibonacci chuẩn.
  • 68. Đặt k = j-i. Đặt fi = a, fi+1 = b. Ta có fi = a, fi+1 = b, fi+2 = fi+1 + fi = a + b, fi+3 = fi+2 + fi+1 = (a+b) + b = a + 2b = a.c2 + b.c3 fi+4 = fi+3 + fi+2 = (a.c2 + b.c3) + (a+b) = a(c2 + 1) + b(c3+1) = a.c3 + b.c4. Tổng quát: fj = fi+k = a.ck-1 + b.ck = fi.ck-1 + b.ck (*) Từ đây suy ra b = (fj - fi.ck-1)/ck Biết fi = a, fi+1 = b ta dễ dàng tính được n như sau: Nếu i < n ta tính dần fi theo chiều xuôi, từ i = i+2 đến n: if (i < n) { for (i = i+2; i <= n; ++i) { t = a + b; a = b; b = t; } return b; } Nếu i > n ta tính theo chiều ngược ta tính dần fi theo chiều ngược, từ i = i-1 lui về n: // i > n for (i = i-1; i >= n; --i) { t = b - a; b = a; a = t; } return a; Chương trình /*------------------------------------------------ Fibonacci --------------------------------------------------*/ #include <iostream> using namespace std; int Fb(int k, int fi, int fj) { int t = 1, s = 1; int i, c; for (i = 3; i <= k; ++i) { c = t + s; t = s; s = c; } return (fj-fi*t)/s; } int Fib(int i, int fi, int j, int fj, int n) { if (n == i) return fi; if (n == j) return fj; int t, k, a, b; if (i > j) { t = i; i = j; j = t; t = fi; fi = fj; fj = t; }
  • 69. k = j-i; a = fi; b = Fb(k,fi, fj); // a = f(i) , b = f(i+1) if (i < n) { for (i = i+2; i <= n; ++i) { t = a + b; a = b; b = t; } return b; } // i > n for (i = i-1; i >= n; --i) { t = b - a; b = a; a = t; } return a; } main() { cout << endl << Fib(5, 76, 0, 7, 3); cout << endl << " Fini"; cin.get(); return 0; } Sudoku /* Name: Sudoku Copyright: Author: Nguyen Xuan Huy Date: 25/08/10 14:50 Description: Search Binary Trees */ #include <iostream> #include <fstream> #include <math.h> const char BL = 32; const int mn = 10; typedef int mi1[mn]; typedef mi1 mi2[mn]; mi2 s,a; mi1 c; using namespace std; // Hien thi void Print(mi2 s) { cout << endl; for (int i = 0 ; i < 9; ++i) { cout << endl; for (int j = 0; j < 9; ++j) cout << s[i][j] << BL; }
  • 70. } // cac mau da dung tren dong i void Row(int i) { for (int j = 0; j < 9; ++j) ++c[s[i][j]]; } // cac mau da dung tren dong j void Colum(int j) { for (int i = 0; i < 9; ++i) ++c[s[i][j]]; } // cac mau da dung trong khoi (x,y) void Square(int x, int y) { for (int i = 3*x; i < 3*x+3; ++i) for (int j = 3*y; j < 3*y+3; ++j) ++c[s[i][j]]; } bool TestRow() { for (int i = 0; i < 9; ++i) { memset(c,0,sizeof(c)); for (int j = 0; j < 9; ++j) if (c[s[i][j]]) return false; else ++c[s[i][j]]; } cout << endl << "Rows OK"; return true; } bool TestColum() { for (int j = 0; j < 9; ++j) { memset(c,0,sizeof(c)); for (int i = 0; i < 9; ++i) if (c[s[i][j]]) return false; else ++c[s[i][j]]; } cout << endl << "Colums OK"; return true; } bool TestSquare() { int i, j, x, y; for (x = 0; x < 3; ++x) for (y = 0; y < 3; ++y) { memset(c,0,sizeof(c)); for (i = 3*x; i < 3*x+3; ++i) for (j = 3*y; j < 3*y+3; ++j) if (c[s[i][j]]) return false; else ++c[s[i][j]]; } cout << endl << "Squares OK"; return true; } bool Test() { return TestRow() && TestColum() && TestSquare(); } void Print(mi1 a, int d, int c) {
  • 71. for (int i = d; i <= c; ++i) cout << a[i] << BL; } // xac dinh mau cho o (i,j) int FindColour(int i, int j) { memset(c,0,sizeof(c)); Row(i); Colum(j); Square(i/3,j/3); // Print(c,1,9); cin.get(); for (int k = s[i][j]+1; k <= 9; ++k) if (c[k] == 0) return k; return 0; } /*--------------------------- Sinh nhanh 1 cau hinh chuan +-----------------------+ | 1 2 3 | 4 5 6 | 7 8 9 | | 4 5 6 | 7 8 9 | 1 2 3 | | 7 8 9 | 1 2 3 | 4 5 6 | |-------+-------+-------| | 2 3 4 | 5 6 7 | 8 9 1 | | 5 6 7 | 8 9 1 | 2 3 4 | | 8 9 1 | 2 3 4 | 5 6 7 | |-------+-------+-------| | 3 4 5 | 6 7 8 | 9 1 2 | | 6 7 8 | 9 1 2 | 3 4 5 | | 9 1 2 | 3 4 5 | 6 7 8 | +-----------------------+ */ void Gen(int n) { int n2 = n*n; for (int i = 0; i < n2; i++) for (int j = 0; j < n2; j++) s[i][j] = (i*n + i/n + j) % n2 + 1; } /*------------------------------ s: ma tran Sudoku a: ma tran cho truoc cac gia tri a[i] > 0 can bao luu --------------------------------*/ void Read(){ ifstream f("sudoku.inp"); for (int i = 0; i < 9; ++i) for (int j = 0; j < 9; ++j) { f >> s[i][j]; a[i][j] = s[i][j]; } f.close(); } // Chuyen tu o (i,j) sang o ke tiep void Next(int &i, int &j) { if (j < 8) ++j; else { ++i; j = 0; } } // chuyen tu o (i,j) sang o ke truoc void Pred(int &i, int &j) { if (j > 0) --j; else { --i; j = 8; } }
  • 72. void Run() { // back tracking Read(); // Print(s); Print(a); int i = 0, j = 0, cc; while(true) { if (i < 0) { cout << endl << " Vo nghiem"; return; } if (i > 8) { Print(s); if (Test()) cout << endl << "Ok"; else cout << endl << "ERROR !!!"; return; } if (a[i][j]) Next(i,j); // mau co dinh else { // can tim mau cho o [i][j]? cc = FindColour(i,j); if (cc > 0) { // tim duoc s[i][j] = cc; Next(i,j); } else { // ko tim duoc s[i][j] = 0; Pred(i,j); // lui 1 o while (i >= 0 && a[i][j]) Pred(i,j); } } } } main() { Run(); cout << endl << " Fini "; cin.get(); return 0; }