Tải bản đầy đủ (.docx) (27 trang)

PHÉP XOAY MẢNG HAI CHIỀU và một vài bài TOÁN ỨNG DỤNG ti05

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (341.25 KB, 27 trang )

PHÉP XOAY MẢNG HAI CHIỀU
VÀ MỘT VÀI BÀI TOÁN ỨNG DỤNG
A) Đặt vấn đề
Mảng hai chiều là một cấu trúc dữ liệu rất gần gủi với những ai đã từng học lập trình, không
những thế đây còn là cấu trúc quen thuộc để giải quyết khá nhiều bài toán trong các kì thi lập
trình cấp tỉnh, cấp quốc gia hay khu vực. Tuy là cấu trúc quen thuộc nhưng không phải học
sinh nào cũng có thể sử dụng thành thạo khi gặp một bài toán có sử dụng cấu trúc này, đặc
biệt hơn là khi gặp các bài toán cần xoay mảng hai chiều. Để giúp các em có hướng tiếp cận
với các dạng bài toán xoay mảng hai chiều và làm quen với cách truy xuất các phần tử khi
mảng được xoay 45 hay 90 độ, chúng tôi đã chọn chuyên đề PHÉP XOAY MẢNG HAI
CHIỀU VÀ MỘT VÀI BÀI TOÁN ỨNG DỤNG.
B) Nội dung chính
I) Một số công thức quay ma trận
1) Quay phải 45 độ
a. Bài toán:
Cho ma trận có kích thước n*m, nhiệm vụ của bạn là in ra ma trận sau khi đã xoay ma trận
ban đầu 45 độ sang phải.
INPUT
33

OUTPUT

b. Xây dựng công thức
Để tiện cho quá trình xây dựng công thức ta gọi:
 (i,j) là tọa độ của ma trận ban đầu.
 (xp,yp) là tọa độ của ma trận sau khi xoay phải 45 độ
Ta vẽ bảng mô tả quá trình xoay
1|Page


Phép xoay mảng hai chiều và một vài bài toán ứng dụng



Hình 1.1. Ma trận ban đầu a

Hình 1.2. Ma trận đã xoay b

Từ đây ta rút ra được công thức chuyển: Ô ở vị trí (i,j) của ma trận acó kích thước là
n*m, sẽ chuyển thành ô có tọa độ (xp,yp) ở ma trận bcó kích thước (n+m-1)* (n+m1)với công thức:
(xp,yp) = (i + j – 1, n - i +
j)
c. Code tham khảo
#include <bits/stdc++.h>
#define FOR(i,a,b) for (int i=(a);i<=(b);i++)
#define xp i + j - 1
#define yp n - i + j
using namespace std;
void htm(int a[][100], int n, int m)
{
cout <<"=================" << endl;
FOR(i,1,n)
{
FOR(j,1,m)
{
cout <}
cout <<'\n';
}
}

2|Page



Phép xoay mảng hai chiều và một vài bài toán ứng dụng
int main()
{
int n=3;
int m=6;
cin >> n >> m;
int a[100][100],b[100][100];
//KHOI TAO: chúng ta tạo sẵn ma trận để dễ kiểm tra
int val=0;
FOR(i,1,n)
FOR(j,1,m) a[i][j]=++val;
//Hiển thị ma trận ban đầu
htm(a,n,m);
//Tính ma trận b
FOR(i,1,n)
FOR(j,1,m) b[xp][yp] = a[i][j];
//Hiển thị ma trận b sau khi xoay
htm(b,n+m-1,n+m-1);
return 0;
}

2) Quay trái 45 độ
a. Bài toán:
Cho ma trận có kích thước n*m, nhiệm vụ của bạn là in ra ma trận sau khi đã xoay ma
trận ban đầu 45 độ sang trái.

3|Page



Phép xoay mảng hai chiều và một vài bài toán ứng dụng

INPUT
34

OUTPUT

b. Xây dựng công thức
Để tiện cho quá trình xây dựng công thức ta gọi:
 (i,j) là tọa độ của ma trận ban đầu.
 (xp,yp) là tọa độ của ma trận sau khi xoay trái 45 độ
Ta vẽ bảng mô tả quá trình xoay
Hình 2.1. Ma trận ban đầu a

Ma trận đã xoay b

Từ đây ta rút ra được công thức chuyển: Ô ở vị trí (i,j) của ma trận a có kích thước là
n*m, sẽ chuyển thành ô có tọa độ (xp,yp) ở ma trận b có kích thước (n+m-1)* (n+m-1)
với công thức:
(xp, yp) = (m+i-j, i+j1)
4|Page


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

c. Code tham khảo
#include <bits/stdc++.h>
#define FOR(i,a,b) for (int i=(a);i<=(b);i++)
#define xp m+i-j
#define yp i+j-1

using namespace std;
void htm(int a[][100], int n, int m)
{
cout <<"=================" << endl;
FOR(i,1,n)
{
FOR(j,1,m)
{
cout <}
cout <<'\n';
}
}
int main()
{
int n=3;
int m=4;
int a[100][100],b[100][100];
//KHOI TAO
int val=0;
FOR(i,1,n)
FOR(j,1,m) a[i][j]=++val;
//Hien thi ma tran goc
htm(a,n,m);
//Tính ma trận b
FOR(i,1,n)

5|Page



Phép xoay mảng hai chiều và một vài bài toán ứng dụng
FOR(j,1,m) b[xp][yp] = a[i][j];
// Hien thi ma trận b
htm(b,n+m-1,n+m-1);
return 0;
}

3) Quay phải 90 độ
a. Bài toán:
Cho ma trận có kích thước n*m, nhiệm vụ của bạn là in ra ma trận sau khi đã xoay ma
trận ban đầu 90 độ sang phải.
INPUT
34

OUTPUT

b. Xây dựng công thức
Để tiện cho quá trình xây dựng công thức ta gọi:
 (i,j) là tọa độ của ma trận ban đầu.
 (xp,yp) là tọa độ của ma trận sau khi xoay phải 90 độ
Ta vẽ bảng mô tả quá trình xoay
Ma trận ban đầu a

Ma trận đã xoay b

6|Page


Phép xoay mảng hai chiều và một vài bài toán ứng dụng


Từ đây ta rút ra được công thức chuyển: Ô ở vị trí (i,j) của ma trận a có kích thước là
n*m, sẽ chuyển thành ô có tọa độ (xp,yp) ở ma trận b có kích thướcm*n với công thức:
(xp, yp) = (j, n - i +
1)
c. Code tham khảo
#include <bits/stdc++.h>
#define FOR(i,a,b) for (int i=(a);i<=(b);i++)
#define xp j
#define yp n - i + 1
using namespace std;
void htm(int a[][100], int n, int m)
{
cout <<"=================" << endl;
FOR(i,1,n)
{
FOR(j,1,m)
{
cout <}
cout <<'\n';
}
}
int main()
{
int n=3;
int m=4;
int a[100][100],b[100][100];
//KHOI TAO
int val=0;
FOR(i,1,n)

FOR(j,1,m) a[i][j]=++val;

7|Page


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
//Hien thi ma tran goc
htm(a,n,m);
//Tính ma trận b
FOR(i,1,n)
FOR(j,1,m) b[xp][yp] = a[i][j];
//Hiển thị ma trận b
htm(b,m,n);
return 0;
}

4) Quay trái 90 độ
a. Bài toán:
Cho ma trận có kích thước n*m, nhiệm vụ của bạn là in ra ma trận sau khi đã xoay ma
trận ban đầu 90 độ sang trái.
INPUT
34

OUTPUT

b. Xây dựng công thức
Để tiện cho quá trình xây dựng công thức ta gọi:
 (i,j) là tọa độ của ma trận ban đầu.
 (xp,yp) là tọa độ của ma trận sau khi xoay trái 90 độ
Ta vẽ bảng mô tả quá trình xoay


Ma trận ban đầu a

Ma trận đã xoay b
8|Page


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

Từ đây ta rút ra được công thức chuyển: Ô ở vị trí (i,j) của ma trận a có kích thước là
n*m, sẽ chuyển thành ô có tọa độ (xp,yp) ở ma trận b có kích thước m*n với công thức:
(xp, yp) = (m-j+1,
i)
c. Code tham khảo
#include <bits/stdc++.h>
#define FOR(i,a,b) for (int i=(a);i<=(b);i++)
#define xp m-j+1
#define yp i
using namespace std;
void htm(int a[][100], int n, int m)
{
cout <<"=================" << endl;
FOR(i,1,n)
{
FOR(j,1,m)
{
cout <}
cout <<'\n';
}

}
int main()
{
int n=3;
int m=4;

9|Page


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
int a[100][100],b[100][100];
//KHOI TAO
int val=0;
FOR(i,1,n)
FOR(j,1,m) a[i][j]=++val;
//Hien thi ma tran goc
htm(a,n,m);
// Tính ma trận b
FOR(i,1,n)
FOR(j,1,m) b[xp][yp] = a[i][j];
// Hiển thị ma trận b
htm(b,m,n);
return 0;
}

II) Một số bài toán vận dụng
1) Chefland và khoảng cách trung bình
a. Đề bài
Chefland là một lưới gồm N dòng và M cột. Mỗi ô của lưới hoặc rỗng hoặc chứa một
ngôi nhà. Khoảng cách giữa hai ngôi nhà là khoảng cách Manhattan giữa hai ô chứa

chúng. Với mỗi d nằm giữa 1 và N + M – 2 (tính cả hai biên), Chef muốn tính số cặp
ngôi nhà khác nhau có khoảng cách bằng d. Hãy giúp anh ấy nhé!
Dữ liệu vào
 Dòng đầu tiên của dữ liệu vào chứa một số nguyên T – số test. T test được miêu
tả như sau:
 Dòng đầu tiên của mỗi test chứa hai số nguyên N và M.
 N dòng tiếp theo. Dòng thứ i (1 ≤ i ≤ N) chứa một xâu nhị phân có độ dài M, với
mỗi j (1 ≤ j ≤ M), ký tự thứ j của xâu là ‘1’ nếu ô ở hàng thứ i và cột thứ j chứa
một ngôi nhà hoặc ‘0’ nếu nó rỗng.
Dữ liệu ra

10 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

 Với mỗi test, in ra một dòng chứa N + M – 2 số nguyên. Với mỗi i, số nguyên
thứ i thể hiện số cặp có khoảng cách bằng i.
Ràng buộc
1≤T≤3
 2 ≤ N, M ≤ 50
b. Hướng dẫn giải
Với bài này có khá nhiều hướng giải quyết, tuy nhiên ở chuyên đề này tôi xin sử dụng
phương pháp xoay ma trận để giải quyết bài toán này.
Xin nhắc lại khái niệm Khoảng cách Manhattan cho những ai quan tâm:
Khoảng cách Manhattan, còn được gọi là khoảng cách trong thành phố, là một dạng
khoảng cách giữa hai điểm trong không gian Euclid với hệ tọa độ Descartes. Đại lượng
này được tính bằng tổng chiều dài của hình chiếu của đường thẳng nối hai điểm này trong
hệ trục tọa độ Descartes
Ví dụ khoảng cách Manhattan của hai điểm có tọa độ P1(x1,y1) và P2(x2,y2) được tính

bằng công thức
|x1-x2| + |y1-y2|

Hình minh họa
Giả sử ta có ma trận ban đầu gồm 10 dòng, 9 cột như bên dưới.
11 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

 Khoảng cách Manhattan có độ dài là 1 từ ô (5,4) là các ô được bôi màu:

 Khoảng cách Manhattan có độ dài là 2từ ô (5,4) là các ô được bôi màu:

Theo như mô tả trên , ta có thể thấy rằng, số lượng các thành phố cách thành phố (i,j)
khoảng là d chính là số lượng số 1 trên hình thoi cách tâm d vị trí.

12 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

Để tiện cho việc đếm số lượng số 1 trên hình thoi, ta tiến hành xoay 45 độ qua phải, lúc
đó hình thoi chúng ta cần tính trở thành hình vuông  dễ dàng đếm số lượng phần tử 1
trên cạnh hình vuông.
Với ví dụ ban đầu có thể mô hình hóa thành ma trận như sau:

Ma trận trước khi xoay

Ma trận sau khi xoay 45 độ sang phải


 Đánh giá độ phức tạp:
Với thuật toán duyệt từng khoảng cách d trong miền từ 1 đến N+M-2  O(d)
ta duyệt từng vị trí có thành phố trong ma trận đã xoay  O((N+M)^2)
ta đếm số lượng số 1 của 4 cạnh xung quanh hình vuông kích thước d  O(d)
Từ đây, ta suy ra độ phức tạp của toàn bài O(d^2 * (N+M)^2)
Với ràng buộc bài toán hiện tại ta vẫn có thể giải quyết được.
Bên cạnh giải thuật trên, bài toán còn có thể tối ưu để giải quyết với ràng buộc
2<=N,M <= 300, chi tiết tham khảo: />c. Code tham khảo
Code tham khảo với hướng xử lý xoay ma trận
#include <bits/stdc++.h>

13 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define maxma 601
//xoay 45 do ben phai
#define xp i + j - 1
#define yp n - i + j
using namespace std;
int count1square(short b[][maxma], int i,int j,int d,int n)
{
int kq = 0;
int vtd,vtc;
//canh tren
vtd=i-d;
if(vtd>=1){
for(vtc=max(j-d,1);vtc<=n && vtc<=j+d;vtc++)

{
if(b[vtd][vtc]==1) kq++;
}
}
//canh duoi
vtd=i+d;
if (vtd <=n)
{
for(vtc=max(j-d,1);vtc<=n && vtc<=j+d;vtc++)
{
if(b[vtd][vtc]==1) kq++;
}
}
//canh trai
vtc=j-d;
if (vtc>=1)
{
for(vtd=max(i-d+1,1);vtd<=n && vtd<=i+d-1;vtd++)
{
if(b[vtd][vtc]==1) kq++;
}
}
//canh phai

14 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
vtc=j+d;
if (vtc <=n)

{
for(vtd=max(i-d+1,1);vtd<=n && vtd<=i+d-1;vtd++)
{
if(b[vtd][vtc]==1) kq++;
}
}
return kq;
}
int main()
{
freopen("avgmat.inp","r",stdin);
freopen("avgmat.out","w",stdout);
//khai bao
int t, n, m;
char ch;
cin >> t;
//doc du lieu
FOR(k,1,t)
{
cin >> n >> m;
short a[n][m]; // ma tran du lieu dau vao
short b[maxma][maxma]; // ma tran a da xoay 45 do sang phai
//khoi tao mang b toan so 0
FOR(i,1,n+m-1)
FOR(j,1,n+m-1) b[i][j]=0;
//xu ly
FOR(i,1,n)
FOR(j,1,m)
{
cin >> ch;

if (ch=='1')
{
a[i][j] =1;
}
else
{

15 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
a[i][j] =0;
}
//xoay ma tran 45 do sang phai
b[xp][yp] = a[i][j];
}
/*
tinh ket qua: duyet voi moi khoang cach i can tinh
ta duyet tat ca cac o de kiem tra
*/
int ans;
FOR(d,1,n+m-2)
{
ans =0;
FOR(i,1,n+m-1)
{
FOR(j,1,n+m-1)
{
if (b[i][j]==1)
{

/*
voi moi o, ta dem cac so 1 tren vien
hinh vuong tam (i,j) ban kinh d
*/
ans = ans + count1square(b,i,j,d,n+m-1);
}
}
}
cout << ans/2 << " ";
}
}
return 0;
}

Code tham khảo thuật toán tối ưu:
#include<bits/stdc++.h>
using namespace std;
void query(){

16 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
int n,m;
cin>>n>>m;
vector<long long> distances(m*(n+m));
vector<long long> acc(n+m);
for(int i=0;istring s;
cin>>s;

for(int j=0;jif(s[j]=='1'){

for(int k=0;kdistances[k*(n+m)+(n-1)-i+abs(k-j)]++;
}
for(int s=(n-1)-i;s<(n+m);s++){
acc[s-(n-1)+i]+=distances[j*(n+m)+s];
}
}
}
}
for(int i=1;i<=(n+m-2);i++){
cout<}
cout<}
int main(){
int tc;
cin>>tc;
for(int i=0;iquery();
}
return 0;
}

2) RBULL - VOI 2016 - Trại bò tót ( o/problems/RBULL/ )
a. Đề bài

17 | P a g e



Phép xoay mảng hai chiều và một vài bài toán ứng dụng

Ông Bảo là chủ của một trang trại, đang nuôi một đàn bò trên khu đất hình chữ nhật chia
thành lưới mxn ô vuông đơn vị. Các hàng của lưới được đánh số từ 1 tới m từ trên xuống,
và các cột của lưới được đánh số từ 1 tới n từ trái qua phải. Ô nằm trên giao điểm của
hàng i và cột j được gọi là ô (i, j). Tại tâm của một số ô đã cắm cọc, mỗi cọc để buộc một
con bò. Để bảo vệ đàn bò tót quý của mình khỏi những tên trộm, ông Bảo thuê Hùng tìm
một thửa đất có dạng hình thoi (mà theo quan niệm của ông Bảo là biểu tượng cho may
mắn) trong khu đất để nhốt đàn bò của mình. Thửa đất hình thoi có tâm tại ô (x0, y0) và
bán kính là r là tập hợp tất cả các ô có tọa độ (x, y) thỏa mãn |x – x0| + |y – y0| <= r. Do
bò tót là các con vật rất hung dữ, nên ông Bảo yêu cầu trong thửa đất tìm được không có
hai ô có cọc nào lại có cạnh chung.
Yêu cầu: Giúp Hùng xác định thửa đất có dạng hình thoi nằm trọng vẹn trong khu đất
với số cọc cột bò là nhiều nhất đáp ứng yêu cầu của ông Bảo.
Dữ liệu:
-

Dòng đầu tiên chứa 2 số nguyên m và n xác định kích thước của khu đất của ông

Bảo.
-

Dòng thứ i trong m dòng sau chứa n kí tự liền nhau, mỗi kí tự xác định trạng thái

của một thửa đất: ‘*’ nếu ô đất có cắm cọc và ‘.’ nếu ô đất đó không cắm cọc.
Kết quả: Đưa ra 4 số nguyên S, x0, y0, r được ghi cách nhau một dấu cách, trong đó: S là
tổng số cọc trong thửa đất được chọn; x0, y0 là tọa độ tâm và r là bán kính của thửa đất
đó. Nếu có nhiều lời giải hãy đưa ra một lời giải bất kỳ.

Ví dụ:
INPUT
23

OUTPUT
1230

...
..*
33

4221
18 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

*.*
...
*.*
Ràng buộc:
-

Có 40% số test ứng với 40% số điểm của bài có 1 <= m,n <= 100

-

Có 40% số test khác ứng với 40% số điểm của bài có 1 <= m,n <= 500

-


Có 20% số test còn lại ứng với 20% số điểm của bài có 1 <= m,n <= 1000

b. Hướng dẫn giải
Việc đầu tiên ta phải xoay bảng 1 góc 45 độ thì mới có thể tính tổng trong 1 hình thoi (khi
đó đã chuyển thành hình vuông) được
Ô (u, v) ⇒ (u+v-1, n-u+v) (nháp sẽ có công thức này) : xoay giấy sang phải 45 độ
4 mảng F1(i, j), F2(i, j), F3(i, j), F4(i, j) là bán kính lớn nhất thỏa mãn về 4 phía, tức là
tam giác vuông về 4 phía thỏa mãn ko có 2 ô cạnh nhau nào cùng có cọc
Xem chi tiết cách tính 4 mảng F bằng QHĐ trong code.
Sau khi đã tính mảng F, ta duyệt hết n*m ô vuông, coi ô đang duyệt (i, j) là tâm hình thoi
⇒ bán kính hình thoi bằng min 4 mảng F(i, j)
Ta sử dụng mảng tổng đã tính trước để tính tổng khi có hình thoi tâm i, j bán kính r và
update kết quả
c. Code tham khảo

19 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
#include <bits/stdc++.h>
#define N 2005
using namespace std;
int n, m, n_new, m_new, res, u_res, v_res, r_res;
int

a[N][N], b[N<<1][N<<1],

s[N<<1][N<<1], F1[N][N],


F2[N][N], F3[N][N],

F4[N][N];
int calc(int u, int v, int r)
{
int lef = v - r + 1;
int rig = v + r - 1;
int xx = rig + u - 1;
int yy = n - u + rig;
int XX = lef + u - 1;
int YY = n - u + lef;
return s[xx][yy] - s[XX-1][yy] - s[xx][YY-1] + s[XX-1][YY-1];
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
//freopen("INP.TXT", "r", stdin);
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
char c; int x = 0; cin >> c;
if (c == '*') x = 1;
a[i][j] = x;
b[i + j - 1][n - i + j] = x;
}
for (int i = 1; i <= m + n - 1; i++)
for (int j = 1; j <= m + n - 1; j++)
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + b[i][j];
for (int i = 1; i <= n; i++)

for (int j = 1; j <= m; j++)

20 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
if (a[i][j] && (a[i-1][j] || a[i][j-1])) F1[i][j] = 1;
else F1[i][j] = min(F1[i-1][j], F1[i][j-1]) + 1;
for (int i = 1; i <= n; i++)
for (int j = m; j >= 1; j--)
if (a[i][j] && (a[i][j+1] || a[i-1][j])) F2[i][j] = 1;
else F2[i][j] = min(F2[i-1][j], F2[i][j+1]) + 1;
for (int i = n; i >= 1; i--)
for (int j = 1; j <= m; j++)
if (a[i][j] && (a[i][j-1] || a[i+1][j])) F3[i][j] = 1;
else F3[i][j] = min(F3[i+1][j], F3[i][j-1]) + 1;
for (int i = n; i >= 1; i--)
for (int j = m; j >= 1; j--)
if (a[i][j] && (a[i][j+1] || a[i+1][j])) F4[i][j] = 1;
else F4[i][j] = min(F4[i+1][j], F4[i][j+1]) + 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
int r = min(F1[i][j], min(F2[i][j], min(F3[i][j], F4[i][j])));
int sum = calc(i, j, r);
if (sum > res)
{
res = sum;
u_res = i;
v_res = j;

r_res = r-1;
}
}
cout <}

3) The Lazy Cow
( />21 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

a. Đề bài:
Hôm nay là một ngày hè nóng nực nên cô bò Bessie có cảm giác lười biếng. Cô ta muốn
đặt mình vào một vị trí trên cánh đồng của cô ta để có thể vươn tới được nhiều đám cỏ
ngon nhất có thể chỉ với một quãng đường ngắn.
Cánh đồng của Bessie có kích thước NxN (1 <= N <= 400). Tại dòng r, cột c của cánh
đồng chứa G(r,c) đơn vị cỏ. Bessie muốn chọn một điểm trên cánh đồng làm vị trí ban
đầu của cô ta sao cho số lượng cỏ mà cô ta có thể đi tới trong vòng K bước là lớn nhất.
Khi Bessie đi một bước, cô ta sẽ di chuyển một đơn vị độ dài về hướng đông, tây, nam,
hoặc bắc ở vị trí hiện tại của cô ta.
Hãy giúp cô bò Bessie tính toán số lượng đơn vị cỏ lớn nhất mà cô ta có thể đi tới nếu cô
ta chọn vị trí tốt nhất trên cánh đồng.
INPUT
*Dòng 1: Số tự nhiên N và K.
*Dòng 2..1+N: mô tả số lượng cỏ của cánh đồng, tại dòng r, cột c là G(r,c)
OUTPUT
*Dòng 1: Số lượng cỏ tối đa mà Bessie có thể tới được từ vị trí ban đầu của cô ta nếu như
cô ta chọn vị trí tốt nhất.
RÀNG BUỘC

1 <= N <= 400
1 <= r,c <= N
0 <= G(r,c) <= 1000
0 <= K <= 2*N
Lazy.inp
5 2
50 5 25 6 17
14 3 2 7 21
99 10 1 2 80
8 7 5 23 11
10 0 78 1 9

Lazy.out
342

22 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng

GIẢI THÍCH
Bessie chọn ô có đánh dấu (B) để đứng thì Bessie có thể đến được các ô được bôi màu
cam Tổng số cỏ có thể ăn được là 342

Hình mô tả
b. Hướng dẫn giải
Hướng tiếp cận 1: Ta duyệt tất cả các ô (r,c) có thể đặt Bessie, tính tổng các đám cỏ
màBessie có thể đến được Độ phức tạp O(N2*K2)
Hướng tiếp cận 2: Ta tính sẵnlượng cỏ có hình thoi như hình vẽ, mỗi điểm ta trượt hình
thoi đến vị trí tiếp theo, tính lại lượng cỏ có thể ăn được ở vị trí tiếp theo. Ở đây, chi phí

để tính không còn là K^2 nữa mà ta sử dụng kĩ thuật bổ sung các ô mới đến được và loại
bỏ các ô củ không đến được. Với hướng tiếp cận này ta có khả năng sẽ giải quyết được
bài toán với ràng buộc như trên.
Tuy nhiên để việc trượt hình thoi dễ dàng, ta thay vì trượt hình thoi, ta xoay ma trận ban
đầu 45 độ sang phải, sau đó việc trượt hình thoi sẽ thành trượt hình vuông có kích thước
KxK.
 Độ phức tạp O(N2*K)
c. Code tham khảo
#include <fstream>
#define MAX 801
using namespace std;
int n,k,mat[MAX][MAX],best;

23 | P a g e


Phép xoay mảng hai chiều và một vài bài toán ứng dụng
int main()
{
ifstream fin("lazy.in");
fin >> n >> k;
// rotate the matrix 45 degrees
for (int i=0; ifor (int j=0; jfin >> mat[i+j][n-i+j-1];
fin.close();
// 45 degree rotation expands the size of the matrix
// it also includes the cells with half distance
int t=(n+1)%2;


// t is used to avoid non-lattice points

n=n*2-1;
for (int i=0; i{
// calculate the sum of 2k x 2k region
// Bessie can only be positioned in lattice points
int sum=0;
for (int a=max(i-k,0); afor (int b=max(t-k,0); bsum+=mat[a][b];
if (best// slide the region
for (int j=t+1; j+k{
for (int a=max(i-k,0); a{
if (j-k-1>=0) sum-=mat[a][j-k-1];
sum+=mat[a][j+k];
}
// update the sum only in lattice points
if (j%2==t && best}
}
ofstream fout("lazy.out");
fout << best << endl;

24 | P a g e



Phép xoay mảng hai chiều và một vài bài toán ứng dụng
fout.close();
}

C) Kết luận
Nội dung đề tài tập trung việc làm quen với thao tác trên mảng hai chiều để giải quyết các
bài toán có thay đổi trên mảng, cụ thể:





Xoay phải 45 độ
Xoay trái 45 độ
Xoay phải 90 độ
Xoay trái 90 độ

Chúng tôi nhận thấy các đề quốc gia các năm gần đây cũng thường đề cập đến mảng hai
chiều, tuy bài tập áp dụng cụ thể việc xoay ma trận với các góc phía trên không nhiều,
nhưng thông qua chuyên đề, chúng tôi mong muốn các em học sinh làm quen dần với
việc xác định lại vị trí các phần tử trên mảng khi có sự thay đổi nào đó trên mảng.
Ưu điểm:
Chuyên đề giúp các em làm quen với xác định các phần tử trên mảng hai chiều khi xoay
mảng, thông qua đó các em hình thành dần kĩ năng thao tác trên mảng hai chiều để vận
dụng vào các kì thi có sự dụng mảng hai chiều một cách hiệu quả.
Nhược điểm:
Bài tập vận dụng vẫn chưa nhiều để thấy rõ được ý nghĩa của chuyên đề. Bên cạnh đó,
đây chỉ là một kĩ thuật nhỏ để giải quyết bài toán, trong bài tập thực tế cần kết hợp nhiều
kĩ thuật và kĩ năng khác để giải quyết bài toán một cách triệt để.
D) TÀI LIỆU THAM KHẢO

1. />2. o/problems/RBULL/
3. />
25 | P a g e


×