Upload
nguyen-huu-tuan
View
71
Download
4
Embed Size (px)
Citation preview
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 1 -
Bài toán ậ con ổ ớ ấ
(Maximum sub rectangle)
19/03/2010)
“There are wavelengths that people cannot see, there are sounds that people cannot hear, and maybe computers have thoughts that people cannot think” Dr. Richard W. Hamming (1915-1998)
Qui hoạc ộng c c .
1. ậ ổ ớ ấ
c c c c ộ c
c c c c c nguyên thuộc khoảng [-127, 127] (1 < N, M ≤ 100)
ộ c (gồm các ph n t li n k c a ma tr u) c c c
c .
Ma tr u là
Thì ma tr n con có t ng l n
T ng các ph n t c a ma tr n kết quả 15 T ờng h p có nhi u kết quả thì
chỉ c n l y kết quả u tiên.
Thu t toán thứ nh t cho thu ơ ản: chúng ta xét hết t t cả các ma tr n con
có th có c a ma tr u, tính t ơ ứng c a chúng và tìm ra t ng l n nh t. Các
bạ ộc giả có th dễ dàng nh n th y rằng thu t toán này s c ộ phức tạp O(N, M) = N3 x
M3. V i giá trị N = M = 100 ộ phức tạp thu t toán s là O(N, M) = 102*3*2 = 1012, và t t
nhiên con s này là không thực tế i v i các máy PC hiện nay.
T c c ạ ạc ộ ả ế
tìm ma tr n con có t ng l n nh t ộ ức ạ ờ ả
2. Thuật toán 1: quy hoạ động dựa trên việ í ước tổng của các ma trận
con xuất phát từ phần tử ở vị trí (0, 0).
Thu t toán thứ nh giải quyết bài toán dựa trên việc xây dựng một mả [][] c
ị ĩ
,
0 i*j=0
[ ][ ] [ ][ ]r i c j
dp i j d r c
(1)
Ở â ảng d[][] là mảng các ph n t c a ma tr u (chỉ s ừ 0).
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 2 -
Việc tính các giá trị c a mả [][] c thực hiện theo công thức truy hồi sau:
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1]+d[i-1][j-1]; (2)
V i mả [][] c c này chúng ta có th c t ng các ph n t c a một
ma tr n con nằm từ cột j t i cột j+col và từ hàng i t i hàng i + row theo công thức sau:
sum = dp[i+row+1][j+col+1] -dp[i+row+1][j]-dp[i][j+col+1] +dp[i][j]; (3)
Các bạn có th xem hình minh họ hi õ ơ c ức trên.
0, 0
n, m
i, jcol
row
i+row, j+col
K c ú c ỉ c n 4 vòng lặp lồ duyệt qua t t cả các ma tr n con có th
có c a ma tr u, tính t ng các ph n t trong ma tr n con theo công thức (3) ở trên
và tìm ra t ng l n nh t.
C th thu c c ặ
int findMax2(int n, int m) // O(N^2M^2)
{
int maxSum = d[0][0];
int sum;
int i, j;
int col, row;
// initial dp array
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1]+d[i-1][j-1];
cout << endl;
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 3 -
for(i=0;i<n;i++)
for(j=0;j<m;j++)
for(row=0;i+row<n;row++)
for(col=0;j+col<m;col++)
{
// watch out here in index of dp array
sum = dp[i+row+1][j+col+1]+dp[i][j]-dp[i+row+1][j]-dp[i][j+col+1];
if(sum>maxSum)
maxSum = sum;
}
return maxSum;
}
Thu c ộ phức tạp O(N, M) = N2 x M2 ờng h = = 100 ộ
phức tạp c th là 108, con s này là có th ch p nh c (chạ ò c ến 0,5
giây).
3. Bài toán dãy con có tổng lớn nhất và thuật toán Kadane cho mảng 1 chiều
T c khi xem xét thu t toán thứ 2 cho bài toán chúng ta s xem xét bài toán tìm dãy
con (gồm các ph n t liên tiếp nhau) có t ng l n nh t trong một mảng một chi u và thu t
toán Kadane (Joseph B. Kadane - http://lib.stat.cmu.edu/~kadane/) cho bài toán này.
Thu t toán Kadane s d ng kỹ thu t quy hoạc ộ ơ ả giải quyết bài toán
theo công thức truy hồi sau:
for x in a[0..n-1]
temp_max = max(0, temp_max) + x;
max_sum = max(max_sum, temp_max);
giá trị 0 c ờng h p mảng không có ph n t nào (t ng các ph n t ứ c x
nhỏ ơ 0) c lại giá trị l n nh t c a mảng tính t i ph n t x s là giá trị l n nh ạt
c v i mảng gồm các ph n t c x cộng thêm v i x.
ặt c th cho thu
int max_subarray(int a[], int n)
{
// end và start là hai biến toàn c c ghi lại vị trí bắ u và kết thúc c a nghiệm
// thu t toán Kadane
int temp_max = 0;
int max_sum = 0;
int i, temp_start = 0;
for(i=0;i<n;i++)
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 4 -
{
temp_max += a[i];
if(temp_max<0)
{
temp_max = 0;
temp_start = i+1;
}
if(temp_max>max_sum)
{
start = temp_start;
end = i;
max_sum = temp_max;
}
}
return max_sum;
}
Thu c ộ phức tạp O(N) = N.
4. Thuật toán Kadane cho bài toán tìm ma trận con có tổng lớn nhất.
Bây giờ chúng ta s áp d ng thu t toán Kadane cho bài toán tìm ma tr n con (hai một
mảng hai chi u) có t ng l n nh t. Ta xem mỗi ma tr n con a[0..n-1, col..(col+span)] gồm các
ph n t a[][] chạy từ hàng 0 (bắ u) t i hàng n-1 (kết thúc) và từ cột col t i cột col+span
c a ma tr u là một mảng một chi u v i các ph n t là t ng c a các ph n t trên
một hàng c a ma tr c ức là:
owSum[ ow] [ ow][ ]col j col span
r r a r j
K ng thu t toán Kadane cho mảng một chi u rowSum[] ta s c mảng
con gồm các ph n t liên tiếp có t ng l n nh t c a mả c n con có
t ng l n nh t c a ma tr n con a[0..n-1][col..(col+span)], và cho biến col chạy từ 0 t i m-1 ta
s có kết quả c a bài toán là giá trị l n nh c qua mỗi l n lặp.
Các bạn có th xem qua hình minh họa sau:
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 5 -
0, 0
n, m
Xé cộ c span
ộ c + span
sumRow[0]
sumRow[1]
sumRow[n-1]
ặt c th c a thu t toán (bằng ngôn ngữ C++) là:
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
const int MAXN = 5000;
const int MAXVAL = 30;
int d[MAXN][MAXN];
int dp[MAXN][MAXN];
int rowSum[MAXN]; // sum of row
void genTest(int n, int m);
// gen data for array d[0..n-1][0..m-1]
void printArray(int i, int j, int n, int m);
int topLeftX, topLeftY, botRightX, botRightY; // location of result
int findMax(int n, int m); // O(NM^2)
int n, m;
int main()
{
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 6 -
clock_t st, et;
n = m = 100;
genTest(n, m);
//printArray(0, 0, n, m);
cout << endl;
st = clock();
cout << findMax(n, m) << endl;
et = clock();
cout << "Thoi gian:" << (et-st)/CLK_TCK;
system("pause");
return 0;
}
int findMax(int n, int m)
{
int col, row, span;
int maxSum = d[0][0];
int sum;
int temp_start;
for(col=0;col<m;col++)
{
// fix column col
// init rowSum array
memset(rowSum,0, n*sizeof(int));
for(span=0;span+col<m;span++)
{
sum = 0;
// colum: span+col
// recalculate rowSum array
for(row=0;row<n;row++)
rowSum[row] += d[row][col+span];
temp_start = 0; // row 0
for(row=0;row<n;row++)
{
sum += rowSum[row];
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 7 -
if(sum<0)
{
temp_start = row+1;
sum = 0;
}
if(sum>maxSum)
{
maxSum = sum;
topLeftX = temp_start;
topLeftY = col;
botRightX = row+1;
botRightY = col+span+1;
}
}
}
}
return maxSum;
}
void genTest(int n, int m)
{
srand((int)time(NULL));
int i, j;
for(i=0;i<n;++i)
for(j=0;j<m;++j)
d[i][j] = (rand()%MAXVAL) - MAXVAL/2;
}
void printArray(int i, int j, int n, int m)
{
int i1, j1;
for(i1=i;i1<n;++i1)
{
for(j1=j;j1<m;++j1)
cout << d[i1][j1] << " ";
cout << endl;
Bài toán ma trận con có tổng lớn nhấ –
[email protected] - 8 -
}
}
Mặc ù ết sức th n trọng và xem xét kỹ c c g bài viết, tuy
v y vẫn có th không tránh khỏi các sai sót, r t mong nh c sự ý ến c a
các bạ ộc giả. Mọi góp ý, thắc mắc xin g i v ịa chỉ email: [email protected].
Hải Phòng, ngày 14, tháng 03, ăm 2010
Nguyễn Hữu Tuân