Lời giải cho đề thi học sinh giỏi quốc qua môn tin học năm học 2014 – 2015, VOI 2015. MINCUT spoj – VOI 2015 Day 2 – Cắt hình
Nguồn đề bài: http://vn.spoj.com/problems/MINCUT/
1. Đề bài MINCUT spoj
Cho A là lưới ô vuông gồm m dòng và n cột. Các dòng của lưới được đánh số từ 1 đến m, từ trên xuống dưới. Các cột của lưới được đánh số từ 1 đến n, từ trái sang phải. Ô nằm trên giao của dòng i và cột j của lưới, được gọi là ô (i, j), chứa số nguyên không âm ai,j có giá trị không vượt quá 106.
Các lưới ô vuông như vậy luôn là đối tượng cho nhiều nghiên cứu thú vị. Vừa qua, trong giờ học ôn luyện cho kỳ thi học sinh giỏi Tin học, Hùng được cô giáo giao cho giải quyết bài toán trả lời truy vấn sau đây đối với bảng đã cho:
Cho một hình chữ nhật con có ô trái trên là ô (x,y) và ô phải dưới là ô (u,v), cần đưa ra chênh lệch nhỏ nhất trong số các chênh lệch giữa hai tổng các số trong hai hình chữ nhật thu được bằng việc cắt ngang hoặc cắt dọc hình chữ nhật đã cho dọc theo đường kẻ của lưới. Giả thiết (x,y) và (u,v) là hai ô khác nhau trên lưới.
Bạn hãy giúp Hùng giải quyết bài toán đặt ra.
Yêu cầu: Cho lưới A và k bộ xq, yq , uq, vq (q = 1, 2, …, k) tương ứng với k truy vấn, hãy đưa ra các câu trả lời cho k truy vấn.
Dữ liệu vào
- Dòng đầu tiên chứa ba số nguyên m, n, k (k ≤ m×n);
- m dòng tiếp theo, dòng thứ i chứa n số nguyên không âm ai1, ai2, …, ain;
- Dòng thứ q trong số k dòng tiếp theo chứa 4 số nguyên xq, yq, uq, vq (q = 1, 2, …, k).
Dữ liệu ra
- Ghi ra file văn bản MINCUT.OUT gồm k dòng, mỗi dòng chứa một số là câu trả lời cho một truy vấn theo thứ tự xuất hiện trong file dữ liệu vào.
Ràng buộc
- Có 30% số test ứng với 30% số điểm của bài có m, n ≤ 10.
- Có 30% số test khác ứng với 30% số điểm của bài có m, n ≤ 100.
- Có 40% số test ứng với 40% số điểm còn lại của bài có m, n ≤ 1000.
Ví dụ
Input
3 3 2
1 1 1
1 1 1
1 1 1
1 1 3 3
1 1 3 2
Output
3
0
2. Hướng dẫn MINCUT spoj
– Xây dựng mảng tổng A[i,j] là tổng của hình chữ nhật từ ô 1;1 đến ô i;j. giống như bài BONUS trên vn spoj.
vừa nhập vừa xử lí có thể viết như sau:
for i:=1 to m do
for j:=1 to n do
begin
read(f,a[i,j]);
A[i,j]:=A[i,j]+A[i-1,j]+a[i,j-1]-A[i-1,j-1];
end;
– Sau đó chặt nhị phân để tìm độ chênh lệch nhỏ nhất. ý tưởng là vậy, cách chặt nhị phân các bạn có thể tham khảo code mẫu.
Độ phức tạp thuật toán: O(n^2 log2 N).
3. code tham khảo MINCUT spoj
a. Code Pascal
const fi=''; nmax=1000; type data=longint; var f:text; A:array[0..nmax+1,0..nmax+1] of int64; n,m,k:data; function GetS(x,y,u,v:data):int64; begin gets:=A[u,v]-A[u,y-1]-a[x-1,v]+a[x-1,y-1]; end; function min(a,b:int64):int64; begin if a<b then exit(a); exit(b); end; procedure xuli(x,y,u,v:data); var i,j:data; res,Sfull,stam:int64; dau,cuoi,giua:data; begin Sfull:=getS(x,y,u,v); res:=high(int64); dau:=x; cuoi:=u-1; while dau<=cuoi do begin giua:=(dau+cuoi) shr 1; stam:=getS(x,y,giua,v); if 2*stam<=sfull then begin res:=min(res,sfull-2*stam); dau:=giua+1; end else cuoi:=giua-1; end; dau:=x; cuoi:=u-1; while dau<=cuoi do begin giua:=(dau+cuoi) shr 1; stam:=getS(x,y,giua,v); if 2*stam>=sfull then begin res:=min(res,2*stam-sfull); cuoi:=giua-1; end else dau:=giua+1; end; dau:=y; cuoi:=v-1; while dau<=cuoi do begin giua:=(dau+cuoi) shr 1; stam:=gets(x,y,u,giua); if 2*stam>=sfull then begin res:=min(res,2*stam-sfull); cuoi:=giua-1; end else dau:=giua+1; end; dau:=y; cuoi:=v-1; while dau<=cuoi do begin giua:=(dau+cuoi) shr 1; stam:=gets(x,y,u,giua); if 2*stam<=sfull then begin res:=min(res,sfull-2*stam); dau:=giua+1 end else cuoi:=giua-1; end; writeln(res); end; Procedure docfile; var i,j,x,y,u,v:data; begin assign(f,fi); reset(f); read(f,m,n,k); for i:=0 to m+1 do A[i,0]:=0; for j:=0 to n+1 do a[0,j]:=0; for i:=1 to m do for j:=1 to n do begin read(f,a[i,j]); A[i,j]:=A[i,j]+A[i-1,j]+a[i,j-1]-A[i-1,j-1]; end; for i:=1 to k do begin read(f,x,y,u,v); xuli(x,y,u,v); end; close(f); end; begin docfile; end.
b. Code C++
#include <stdio.h> long long s[1001][1001]; int n,m,k,u,v,z,t; long long value(int u,int v,int z,int t){ return s[z][t]-s[z][v-1]-s[u-1][t]+s[u-1][v-1]; } int main(){ scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++){ scanf("%lld",&s[i][j]); s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1]; } while (k--){ scanf("%d%d%d%d",&u,&v,&z,&t); long long res=1000000000000,tmp=value(u,v,z,t); for (int type=0;type<2;type++){ int d=(type==0)?(u):(v),c=(type==0)?(z-1):(t-1),g; while (d<=c){ g=(d+c)/2; long long s=(type==0?value(u,v,g,t):value(u,v,z,g))*2-tmp; if (s>=0){ res=(s<res)?(s):(res); c=g-1; } else { res=(-s<res)?(-s):(res); d=g+1; } } } printf("%lld\n",res); } return 0; }
Từ khóa:
Lời giải, hướng dẫn , code mẫu, Đề thi quốc gia môn tin học năm học 2014 – 2015, VOI 2015
ple. anh Tiến ơi!
anh viết rõ ràng 1 xíu đc k?
em biết bài phần thưởng thi QG năm nào ko 🙂
e có
nhưng e k hiểu chặt ấy! còn phần thưởng. hình như e làm rồi :V
chủ yếu là chặt sao cho chênh lệch nhỏ nhất thôi em 🙂 phần chặt em phải ráng 😀
à! ok men! e hiểu rồi!