Chia mảng tỉ lệ 1:k

Một phần của tài liệu Thuật toán pascal cơ bản nâng cao khó (Trang 21 - 26)

Tìm cách chia dãy số nguyên không âm a1, a2,...,an, n > 1 cho trước thành hai đoạn có tổng các phần tử trong một đoạn gấp k lần tổng các phần tử trong đoạn kia, k nguyên dương.

Đặc tả

Gọi t là tổng các phần tử của dãy a, t = sum(a[1..n])

Muốn chia a thành hai đoạn a[1..i] và a[i + 1..n] có tổng gấp nhau k lần ta phải có:

1. t chia hết cho (k + 1). Đặt t1 = t div (k + 1) và tk = t - t1.

2. (#E i: 1 <= i <= n): sum(a[1..i]) = t1 hoặc sum(a[1..i]) = tk.

Để ý rằng nếu k = 1 thì t1 = tk; nếu k > 1 thì t1 < tk, do đó bài này là trường hợp riêng của bài trước khi k = 1.

Trong chương trình dưới đây, hàm Chia(k) cho giá trị i nếu mảng a chia được thành hai đoạn a[1..i] và a[(i + 1)..n] có tổng gấp k lần nhau. Trong trường hợp vô nghiệm Chia = -1. Ta gọi i là điểm chia và dùng biến tr (tổng riêng) để tích luỹ tổng các phần tử của đoạn đang xét a[1..i]. Khi tr = t1 bài toán có nghiệm I, ngược lại, khi tr > t1 ta chưa thể kết luận là bài toán vô nghiệm. Trường hợp này ta phải tiếp tục tích luỹ tr để hi vọng đạt được tổng tr = tk. Nếu sau khi tích luỹ ta thu được tr = tk thì bài toán có nghiệm i, ngược lại, khi tr > tk ta kết luận là bài toán vô nghiệm.

Function Chia(n,k: integer): integer;

var i: integer;

t, t1, tk, tr: longint;

begin

Chia := -1;

t := 0; { t = sum(a[1..n]) } for i := 1 to n do t := t+a[i];

if (t mod (k+1) <> 0) then exit; { vo nghiem } { Xu li truong hop co nghiem }

t1 := t div (k+1); { doan tong nho } tk := t - t1; { tk = k * t1}

tr := 0; { tong rieng tr = sum(a[1..i]) } for i := 1 to n do

begin

tr := tr + a[i];

if (tr = t1) or (tr = tk) then begin { lay nghiem i }

Chia:= i; exit;

end;

end;

end;

Ta gọi thủ tục Gen để sinh dữ liệu kiểm thử. Cũng giống như bài trước, ta sẽ sinh ngẫu nhiên dữ liệu kiểm thử cho hai trường hợp: chắc chắn có nghiệm và có thể vô nghiệm. Với trường hợp có thể vô nghiệm ta sinh ngẫu nhiên như bình thường,

for i := 1 to n do a[i] := random(n);

Với trường hợp có nghiệm, ta sinh ngẫu nhiên mảng a gồm hai đoạn:

Đoạn thứ nhất a[1..d] và đoạn thứ hai a[d + 1..n] trong đó d là một điểm chia được sinh ngẫu nhiên

d := random(n div 2)+1; {diem chia}

Ta lại chọn ngẫu nhiên một trong hai trường hợp:

Trường hợp thứ nhất: đoạn thứ nhất gấp k lần đoạn thứ hai.

Trường hợp thứ hai: đoạn thứ hai gấp k lần đoạn thứ nhất.

(* Pascal *)

(*--- Chia mang nguyen a thanh 2 doan

co tong ti le 1:k

--- *) {$B-}

uses Crt;

const MN = 500;

Esc = #27;{ dau thoat } bl = #32; { dau cach } nl = #13#10; { xuong dong } var a: array [0..MN] of integer;

n: integer;

(*--- Sinh ngau nhien n so nguyen khong am cho mang a --- *) Procedure Gen(m,k: integer);

var i,d: integer; t: longint;

begin

n := m; t := 0;

if random(2) = 0 then { vo nghiem } begin

for i := 1 to n do a[i]:= random(n);

exit;

end;

{ co nghiem }

d := random(n div 2)+1; { diem chia } for i := 1 to d do

begin

a[i] := random(n); t := t+a[i];

end;

if (random(2) = 0) then

{ doan a[1..d] gap k lan doan cuoi } a[d] := a[d]+(k-1)*t

else { doan cuoi gap k lan doan a[1..d] } t := k*t;

for i := d+1 to n-1 do begin

a[i] := random(t); t := t-a[i];

end;

a[n] := t;

end;

Procedure Xem; Hiển thị mảng a, tự viết

Function Chia(n,k: integer): integer; Tự viết Procedure Test;

var j,i,k: integer; t: longint;

begin

randomize;

repeat

n := 10 + random(10);

k := random(5)+1;

writeln(nl,' n = ',n,' k = ',k);

Gen(n,k); Xem; i := Chia(n,k);

if i < 0 then writeln('Khong chia duoc') else

begin t := 0;

for j := 1 to i do t := t+a[j];

write('Doan 1: a[1..',i,'].');

writeln(' Tong = ',t);

t := 0;

for j:=i+1 to n do t := t+a[j];

write('Doan 2: a[',i+1,'..',n,'].');

writeln(' Tong = ',t);

end;

until ReadKey = Esc;

end;

BEGIN Test;

END.

// C#

using System;

using System.Collections.Generic;

using System.Text;

namespace SangTao1 {

/*--- * Chia Mang Ti Le 1:k

* Chia mang nguyen khomng am a[1..] thanh * hai doan ti le 1:k hoac k:1

* ---*/

class ChiaMangTiLe1_k {

static void Main(string[] args) {

do {

Run(10, 3);

Console.Write("\n Bam RETURN de tiep tuc, ");

Console.Write("\n Bam T de thoat: ");

} while (Console.ReadLine() != "T");

}

static public void Run(int n, int k) {

if (n < 0 || n > 1000000 || k < 1) return;

int[] a = Gen(n, k);

Print(a);

int d = Chia(a, k);

if (d < 0) {

Console.WriteLine("\n Vo nghiem");

return;

}

Console.WriteLine("\n “+ Test(a, d, k));

}

// Kiem tra k*Sum(a[1..d]) = Sum(a[d+1..n]) ? // hoac Sum(a[1..d]) = k*Sum(a[d+1..n])

static public bool Test(int[] a, int d, int k) {

Console.WriteLine("\n\n Test, k = " + k);

Console.WriteLine(" Diem Chia = " + d);

int t1 = 0;

for (int i = 0; i < d; ++i) t1 += a[i];

int t2 = 0;

for (int i = d; i < a.Length; ++i) t2 += a[i];

Console.WriteLine("Sum1 = {0}, Sum2 = {1}", t1, t2);

return (t1 == k * t2 || t2 == k * t1);

}

static public int Chia(int[] a, int k) {

int t = 0;

foreach (int x in a) t += x;

if (t % (k + 1) != 0) return -1;

int t1 = t / (k + 1); // tong 1 phan chia int t2 = t - t1; // tong phan con lai int tr = 0; // tong rieng

for (int i = 0; i < a.Length; ++i) {

tr += a[i];

if (tr == t1 || tr == t2) return i+1;

}

return -1;

}

static public int[] Gen(int n, int k) {

Random r = new Random();

int[] a = new int[n];

if (r.Next(2) == 0)

{ // khoang 1/2 so test la vo nghiem for (int i = 0; i < n; ++i) a[i] = r.Next(n);

return a;

}

int d = r.Next(n / 2) + 1; //diem chia int t = 0;

int d1 = d - 1;

for (int i = 0; i < d1; ++i)

{ a[i] = r.Next(n); t += a[i]; } if (r.Next(2) == 0)

// doan dau a[1..d]

// gap k lan doan cuoi a[d+1..n]

a[d1] += (k - 1) * t;

else t *= k; // doan cuoi gap k lan doan dau int n1 = n - 1;

for (int i = d; i < n1; ++i)

{ a[i] = r.Next(t); t -= a[i]; } a[n1] = t;

return a;

}

static public void Print(int[] a) tự viết } // ChiaMangTiLel_k

} // SangTao1

CHƯƠNG 2

Một phần của tài liệu Thuật toán pascal cơ bản nâng cao khó (Trang 21 - 26)

Tải bản đầy đủ (PDF)

(282 trang)