Nguồn đề bài: http://vn.spoj.com/problems/QBSELECT/
1. Đề bài QBSELECT Spoj
Cho một bảng hình chữ nhật kích thước 4×n ô vuông. Các dòng được đánh số từ 1 đến 4, từ trên xuống dưới, các cột được đánh số từ 1 đến n từ trái qua phải.
Ô nằm trên giao của dòng i và cột j được gọi là ô (i,j). Trên mỗi ô (i,j) có ghi một số nguyên aij , i =1, 2, 3, 4; j =1, 2, …, n. Một cách chọn ô là việc xác định một tập con khác rỗng S của tập tất cả các ô của bảng sao cho không có hai ô nào trong S có chung cạnh. Các ô trong tập S được gọi là ô được chọn, tổng các số trong các ô được chọn được gọi là trọng lượng của cách chọn. Tìm cách chọn sao cho trọng lượng là lớn nhất.
Ví dụ: Xét bảng với n=3 trong hình vẽ dưới đây:

Cách chọn cần tìm là tập các ô S = {(3,1), (1,2), (4,2), (3,3)} với trọng lượng 32.
Input
Dòng đầu tiên chứa số nguyên dương n là số cột của bảng.
Cột thứ j trong số n cột tiếp theo chứa 4 số nguyên a1j, a2j, a3j, a4j, hai số liên tiếp cách nhau ít nhất một dấu cách, là 4 số trên cột j của bảng.
Output
Gồm 1 dòng duy nhất là trọng lượng của cách chọn tìm được.
Example
Input:
3
-1 9 3
-4 5 -6
7 8 9
9 7 2
Output:
32
Hạn chế
Trong tất cả các test: n ≤ 10000, |aij| ≤ 30000. Có 50% số lượng test với n ≤ 1000.
2. Gợi ý QBSELECT Spoj
– Các bạn dùng thuật toán Quy Hoạch Động trạng thái:
Gọi F[i,x] là trọng lượng lớn nhất nếu xét từ cột 1 đến cột i và trạng thái của cột i được biểu diễn bằng biến x. Công thức Quy Hoạch Động là:
F[i,x]=Max(F[i-1,x’]+Sum(i,x))
Trong đó:
— x và x’ là trạng thái chọn của hai cột liên tiếp nhau (i và i-1) do đó hai trạng thái phải thỏa mãn điều kiện không có hai ô nào được chọn kề nhau.
— Sum(i,x) là trọng lượng tương ứng với trạng thái chọn x của cột i.
3. Code Tham Khảo QBSELECT Spoj
a. Code Pascal
const
fi='';
fo='';
var
f:text;
n,j,t:Longint;
i,k:byte;
c:array[0..15,0..15]of 0..1;
a:array[0..4,0..10001]of longint;
l:array[0..10001,0..15]of longint;
function d(x:byte):longint;
begin
d:=0;
if x and 1 = 1 then d:=d+a[1,j];
if x shr 1 and 1 =1 then d:=d+a[2,j];
if x shr 2 and 1 =1 then d:=d+a[3,j];
if x shr 3 and 1 =1 then d:=d+a[4,j];
end;
begin
for i:=0 to 15 do
for j:=i to 15 do
begin
c[i,j]:=((i and 1) and (i shr 1 and 1))
or ((i shr 2 and 1) and (i shr 1 and 1))
or ((i shr 2 and 1) and (i shr 3 and 1))
or ((j and 1) and (j shr 1 and 1))
or ((j shr 2 and 1) and (j shr 1 and 1))
or ((j shr 2 and 1) and (j shr 3 and 1))
or ((i and j) and 1) or ((i and j)shr 1 and 1)
or ((i and j)shr 2 and 1) or ((i and j)shr 3 and 1);
c[j,i]:=c[i,j];
end;
assign(f,fi);
reset(f);
readln(f,n);
t:=-50000;
for i:=1 to 4 do
begin
for j:=1 to n do
begin
read(f,a[i,j]);
if a[i,j]>t then t:=a[i,j];
end;
readln(f);
end;
close(f);
if t<0 then begin assign(f,fo); rewrite(f); writeln(f,t); close(f); halt; end;
j:=1;
for i:=0 to 15 do
if c[i,0]=0 then l[1,i]:=d(i) else l[1,i]:=0;
for j:=2 to n do
for i:=0 to 15 do
if c[i,0]=1 then l[j,i]:=0 else
begin
t:=d(i);
for k:=0 to 15 do
if c[i,k]=0 then if l[j,i]<l[j-1,k]+t then l[j,i]:=l[j-1,k]+t;
end;
assign(f,fo);
rewrite(F);
t:=l[n,0];
for i:=1 to 15 do if l[n,i]>t then t:=l[n,i];
writeln(f,t);
close(f);
end.b. Code C++
#include <stdio.h>
#include <algorithm>
#define ll long long
#define maxN 10001
#define minC -200000000
using namespace std;
int n;
int a[5][maxN], f[16][maxN], fr[9][9];
bool flag = false;
int getbit(int k, int x){
return (x >> (k-1)) & 1;
}
void inp(){
int inmax = minC;
for (int i = 1; i <= 4; i++)
for (int j = 1; j <= n; j++){
scanf("%d", &a[i][j]);
inmax = max(inmax, a[i][j]);
}
if (inmax < 0){
printf("%d", inmax);
flag = true;
}
}
int ok(int x, int y){
int bit[5], elsebit[5];
for (int v = 1; v <= 4; v++) bit[v] = getbit(v, x);
for (int v = 1; v <= 4; v++) elsebit[v] = getbit(v, y);
for (int v = 1; v <= 4; v++)
if ((bit[v] & elsebit[v]) == 1) return 0;
return 1;
}
int value(int x, int y){
int bit[5], sum = 0;
for (int v = 1; v <= 4; v++) bit[v] = getbit(v, x);
for (int v = 1; v <= 4; v++)
if (bit[v] == 1) sum += a[v][y];
return sum;
}
int bitmask(){
int d[] = {0, 1, 2, 4, 5, 8, 9, 10}, res = minC;
for (int i = 0; i <= 8; i++)
for (int j = 0; j <= 8; j++) fr[i][j] = ok(d[i], d[j]);
for (int i = 0; i <= n; i++)
for (int j = 0; j <= 8; j++){
int t = minC;
for (int k = 0; k <= 8; k++)
if (fr[j][k] == 1 && f[k][i-1] > t) t = f[k][i-1];
f[j][i] = t + value(d[j], i);
}
for (int i = 0; i <= 8; i++) res = max(res, f[i][n]);
return res;
}
int main(){
//freopen("SELECT.INP", "r", stdin);
//freopen("SELECT.OUT", "w", stdout);
scanf("%d", &n);
inp();
if (flag) return 0;
printf("%d", bitmask());
return 0;
}