Hiển thị các bài đăng có nhãn Quy Hoạch Động. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Quy Hoạch Động. Hiển thị tất cả bài đăng

PTIT014C - 2014 Bài C - Xâu con chung dài nhất

Link Sub: http://www.spoj.com/PTIT/problems/PTIT014C/
Người Gửi: Dương Lee

  • Problem:

Xâu ký tự S được gọi là xâu con của xâu ký tự T nếu ta có thể xoá đi một số ký tự trong xâu T để nhận được xâu S. Gọi LCS(X, Y) là độ dài xâu con chung dài nhất của X và Y.  
Yêu cầu: Cho n xâu S_1, S_2, ... ,S_n. Hãy tính G = max{ LCS(S_i, S_j) } với tất cả các chỉ số i khác j thỏa mãn 1 <= i, j <= n.
Input
Dữ liệu vào gồm nhiều bộ dữ liệu tương ứng với nhiều test. Dòng đầu tiên chứa số nguyên K là số bộ dữ liệu. Tiếp theo là K (K≤100) dòng, mỗi dòng là một bộ dữ liệu có cấu trúc như sau:  
- Dòng đầu tiên của nhóm chứa số nguyên.  
- Dòng tiếp theo, mỗi dòng chứa một xâu ký tự độ dài không vượt quá 30, chỉ gồm các ký tự in hoa.
Output
Với mỗi bộ dữ liệu ghi ra trên một dòng, mỗi dòng ghi ra một số nguyên  là câu trả lời tương ứng với bộ dữ liệu trong dữ liệu vào.
Example:
Input
2
2
ALICE
BOB
2
ABCB
BCAB
Output:
0
3

  • Solution:

- Bài này không thấy cho n nên mình coi 1<=n<=1000.
- Áp dụng quy hoạch động cho xâu con chung dài nhất
Tư tưởng:
- F[i][j] là xâu con chung max của xâu S1(0->i-1) và S2(0->j-1)
- Nếu S1[i]==S2[j] thì F[i+1][j+1]=F[i][j]+1;
- Ngược lại thì F[i+1][j+1] nhận giá trị max của F[i+1][j] và F[i][j+1] là xâu con chung lớn nhất của S1(0->i), S2(0->j-1) hoặc S1(0->i-1), S2(0->j).
Xem hình để hiểu rõ hơn về LCS:
LCS - Longest Common Subsequence - Dãy con chung dài nhất

  • Code:

C++:

https://ideone.com/JmO28p
#include <iostream>
#include <math.h>
using namespace std;

int n;
string S[1003];
void read()
{
    cin>>n;
    for (int i=1; i<=n; i++)
        cin>>S[i];
}

int F[31][31];
void init()
{
    for (int i=0; i<=30; i++)
        for (int j=0; j<=30; j++)
            F[i][j]=0;
}

int LCS(string S1, string S2)
{
    init();
    
    for (int i=0; i<S1.length(); i++)
    {
        for (int j=0; j<S2.length(); j++)
        {
            if (S1[i]==S2[j])
                F[i+1][j+1]=F[i][j]+1;
            else
                F[i+1][j+1]=max(F[i][j+1], F[i+1][j]);
        }
    }
    return F[S1.length()][S2.length()];
}

int main ()
{
    int t;
    cin>>t;
    while (1)
    {
        if (t==0) break;
        t--;
        read();
        int maxLCS = 0;
        for (int i=1; i<=n; i++)
        {
            for (int j=i+1; j<=n; j++)
                maxLCS = max(maxLCS, LCS(S[i], S[j]));
        }
        cout<<maxLCS<<endl;
    }
    return 0;
}

JAVA:

...

Python:

...
PTIT121K - Đường đi lớn nhất

PTIT121K - Đường đi lớn nhất

Link Sub: http://www.spoj.com/PTIT/problems/PTIT121K/
Người Gửi: Dương Lee

  • Problem:

Sau những tiết học ban đầu hứng thú với môn Điện tử số về hệ cơ số, MĐ dần thấy nản khi phải đối mặt với các loại mạch và cổng @@. Đầu óc cứ nghĩ đến mấy cái hệ cơ số, MĐ lại nghĩ ra một bài toán khác để thử tài các bạn SV PTIT :D. Bài toán như sau:  
Cho một bảng vuông (n x n) ô (2<=n<=100) các ô ghi các số là 0 hoặc 1. Bạn hãy tìm đường đi từ góc trái trên xuống góc phải dưới theo nguyên tắc chỉ được dịch chuyển sang phải và xuống dưới sao cho các số trên đường đi tạo thành một số nhị phân có giá trị lớn nhất.
Input
-  Dòng đầu tiên ghi giá trị n  
-  n dòng tiếp theo, trên mỗi dòng ghi n số 0 hoặc 1 các số này cách nhau ít nhất một khoảng trắng.
Output
-  Một số duy nhất là giá trị trong hệ cơ số 16 của số nhị phân được tạo thành ở trên.
Example:
Input
5
1 0 1 1 0
0 0 1 0 1
0 0 1 0 1
1 0 0 1 1
1 1 0 1 0
Output:
176

  • Solution:

- Đối với những bài dạng di chuyển chỉ phải - xuống, lên - trái ta có thể dùng quy hoạch động đơn giản để xét các trạng thái của nó tại mỗi vị trí.
- Ta có dãy các nhị phân: F[][] là mảng max dãy nhị phân. F[i][j] = max(F[i-1][j], F[i][j-1])+(char)(arr[i][j]);
- Sau đó là chuyển dãy nhị phân trên thành dãy các mã hệ 16 (Hex). Code dưới đây chuyển xâu bit thành xâu bit có độ dài chia hết cho 4 và xét 4 bít một để chuyển về Hex (Không thừa số 0 ở đầu).

  • Code:

C++:

https://ideone.com/OMPxBJ
#include <iostream>
#include <math.h>
using namespace std;

int n;
int arr[102][102];
void read ()
{
    cin>>n;
    for (int i=1; i<=n; i++)
        for (int j=1; j<=n; j++)
            cin>>arr[i][j];
}

string F[102][102];
string He2 ()
{
    for (int i=1; i<=n; i++)
    {
        F[0][i]="";
        F[i][0]="";
    }
    
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=n; j++)
        {
            char tmp = (arr[i][j]+'0');
            F[i][j] = max(F[i-1][j], F[i][j-1])+tmp;
        }
    }
    return F[n][n];
}

string BinToHex (string x)
{
    string rs = "";
    while (x.size()%4!=0)
        x="0"+x;
    
    for (int i=0; i<x.size(); i=i+4)
    {
        string tmp = x.substr(i, 4);
        int d = 0;
        for (int j=0; j<4; j++)
        {
            d+=((tmp[j]-'0')*pow(2,(3-j)));
        }
        char c;
        if (d>=0 && d<=9)
            c = d+'0';
        else
            c = d-10+'A';
        rs = rs+c;
    }
    if (rs=="") return "0";
    else
    {
        while (1)
        {
            if (rs.size()-1==0 || rs[0]!='0') break;
            rs.erase(rs.begin(), rs.begin()+1);
        }
    }
    return rs;
}

int main ()
{
    read ();
    string He2Max = He2();
//    cout<<He2Max<<endl;
    cout<<BinToHex(He2Max);
    return 0;
}

JAVA:

...

Python:

...
BCROBOT - Đường đi rô-bốt

BCROBOT - Đường đi rô-bốt

Link Sub: http://www.spoj.com/PTIT/problems/BCROBOT/
Người Gửi: Dương Lee

  • Problem:

Bạn vừa tạo ra một bảng để cho rô-bốt có thể tìm đường đi từ ô ở trên cùng – bên trái (ô xuất phát) đến ô ở dưới cùng – bên phải (ô đích). Tuy nhiên, do quên mất một số skill AI mà bạn chỉ lập trình cho rô-bốt có thể đi sang phải 1 ô hoặc xuống dưới 1 ô. Bạn đặt một số chướng ngại vật trên các ô của bảng (dĩ nhiên là rô-bốt ko thể đi vào các ô này), sau đó bạn ngồi quan sát. Tuy nhiên, sau một thời gian, bạn cảm thấy mệt mỏi vì nó bị mắc kẹt và bạn tự hỏi: “Có bao nhiêu đường đi có thể cho rô-bốt từ ô xuất phát tới ô đích” và “Nếu không có, thì liệu rô-bốt có thể đến ô đích nếu nó được lập trình có thể đi lên trên 1 ô và sang trái 1 ô”.  
Vì vậy, bạn quyết định viết 1 chương trình, cho kích thước của bảng n×n với các chướng ngại vật đã được dánh dấu mà rô-bốt không thể đi tới. Đếm số đường đi khác nhau mà rô-bốt có thể đi từ ô xuất phát tới ô đích. Và nếu không có đường đi, bạn phải kiếm tra xem có thể đi từ ô xuất phát tới ô đích nếu có thể sang trái và lên trên. Tuy nhiên, chương trình của bạn không xử lý các số rất lớn, do đó, kết quả phải được lấy dư cho 2^31-1.
Input
Dòng đầu tiên chứa một số nguyên n (1 <= n <= 1000).  N dòng sau, mỗi dòng chứa n kí tự đại, mỗi kí tự diện cho một ô của bảng. Kí tự có thể là ‘.’ hoặc ‘#’. Kí tự ‘.’ nếu ô đó có thể đi, hoặc ‘#’ nếu ô đó là chướng ngại vật. Không có trường hợp có chướng ngại vật ở ô xuất phát và ô đích.
Output
In ra một dòng chứa số nguyên là số đường đi khác nhau từ ô xuất phát tới ô kết thúc (lấy dư cho 2^31-1) hoặc “THE GAME IS A LIE” nếu không thể đi từ ô xuất phát tới ô kết thúc bằng cách chỉ sang phải và xuống dưới nhưng có thể đi nếu chấp nhận thêm cách đi lên trên và sang trái, hoặc INCONCEIVABLE nếu đơn giản là không có đường đi từ ô xuất phát tới ô đích.
Example:
Input
5
.....
#..#.
#..#.
...#.
.....
Output:
6

Input
7
......#
####...
.#.....
.#...#.
.#.....
.#..###
.#.....
Output:
THE GAME IS A LIE
  • Solution:

- Đối với trường hợp đếm số đường đi ta sẽ dùng QHĐ F[i][j] = F[i-1]+F[i][j-1] (Vì chỉ số cách đi đến ô [i,j]=số cách đi từ trái sang và số cách đi từ trên xuống)
- Đối với trường hợp tìm xem có đường đi từ nếu cho phép đi lên trên và xuống dưới: ta sẽ dùng BFS để kiểm tra xem mảng check có duyệt qua ô [n,n] hay không?

  • Code:

C++:

https://ideone.com/D94X0C
#include <iostream>
#include <math.h>
#include <queue>
using namespace std;

#define Du 2147483647

int n;
char arr[1003][1003];
void read ()
{
    cin>>n;
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=n; j++)
        {
            cin>>arr[i][j];
        }
    }
}

long long F[1003][1003];
int check[1003][1003];
void init()
{
    for (int i=0; i<=n; i++)
    {
        for (int j=0; j<=n; j++)
        {
            F[i][j] = 0;
            check[i][j]=0;
        }
    }
}
long long count ()
{
    F[1][0]=1;
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=n; j++)
        {
            if (arr[i][j]=='.')
                F[i][j]=(F[i-1][j]+F[i][j-1])%Du;
        }
    }
    return F[n][n];
}

struct data
{
    int i;
    int j;
};

int x_xq[]={-1, +0, +1, +0};
int y_xq[]={+0, +1, +0, -1};


void BFS (int y, int x)
{
    data tmp;
    tmp.i = y;
    tmp.j = x;
    queue <data> q;
    q.push(tmp);
    check[y][x]=1;
    while (!q.empty())
    {
        data u = q.front();
        q.pop();
        for (int i=0; i<4; i++)
        {
            int x_m = u.j+x_xq[i];
            int y_m = u.i+y_xq[i];
            if (x_m>=1 && x_m<=n && y_m>=1 && y_m<=n && check[y_m][x_m]==0 && arr[y_m][x_m]=='.')
            {
                tmp.i = y_m;
                tmp.j = x_m;
                q.push(tmp);
                check[y_m][x_m] = 1;
            }
        }
    }
}

int main ()
{
    read();
    init();
    long long x = count();
    if (x!=0)
        cout<<x;
    else
    {
        BFS(1, 1);
        if (check[n][n]==1)
            cout<<"THE GAME IS A LIE";
        else
            cout<<"INCONCEIVABLE";
    }
    return 0;
}

JAVA:

...

Python:

...
BCLIQ - Dãy con tăng dài nhất (bản dễ)

BCLIQ - Dãy con tăng dài nhất (bản dễ)

Link Sub: http://www.spoj.com/PTIT/problems/BCLIQ/
Người Gửi: Dương Lee

  • Problem:

Cho một dãy số nguyên gồm N phần tử A[1], A[2], ... A[N]. 
Biết rằng dãy con tăng đơn điệu là 1 dãy A[i1],... A[ik] thỏa mãn 
i1 < i2 < ... < ik và A[i1] < A[i2] < .. < A[ik]. Hãy cho biết dãy con tăng đơn điệu dài nhất của dãy này có bao nhiêu phần tử? 

Input
Dòng 1 gồm 1 số nguyên là số N (1 ≤ N ≤ 1000). 
Dòng thứ 2 ghi N số nguyên A[1], A[2], .. A[N] (1 ≤ A[i] ≤ 10000).
Output
Ghi ra độ dài của dãy con tăng đơn điệu dài nhất.
Example:
Input
6
1 2 5 4 6 2
Output:
4

  • Solution:

Giải thích test ví dụ: Dãy con dài nhất là dãy A[1] = 1 < A[2] = 2 < A[4] = 4 < A[5] = 6, độ dài dãy này là 4.
Áp dụng QHĐ xây dựng hàm F[i] = max (F[i], F[j]+1) (j:=i-1 -> 1 && A[j]<A[i])
=> max (F[]);
Với test trên ta có F[] = {1, 2, 3, 3, 4, 2}
Có nghĩa là: tại A[i] ta tìm các A[j] sao cho A[j]<A[i] mà tại đó độ dài dài nhất của dãy con tăng là F[j] ta thu được F[i] = F[j]+1; (chọn F[i] max)
Độ phức tạp O(N^2)

  • Code:

C++:

https://ideone.com/NukN69

#include <iostream>
#include <algorithm>
using namespace std;
 
int main ()
{
    int n;
    cin>>n;
    long arr[1003];
    long F[1003];
    for (int i=1; i<=n; i++)
        cin>>arr[i];
    arr[0] = 0;
    F[0] = 0;
    for (int i=1; i<=n; i++)
    {
        F[i] = 1;
        for (int j=i-1; j>=1; j--)
        {
            if (arr[i]>arr[j])
            {
                F[i]=max(F[i], F[j]+1);
            }
        }
    }
    
    long dmax = 1;
    for (int i=1; i<=n; i++)
        if (F[i]>=dmax)
            dmax = F[i];
    cout<<dmax;
    return 0;
}

JAVA:


BCINCSEQ - Đoạn tăng

BCINCSEQ - Đoạn tăng

Link Sub: http://www.spoj.com/PTIT/problems/BCINCSEQ/
Người Gửi: Dương Lee

  • Problem:

Cho dãy số nguyên A = (a1, a2, …, an). Hãy tìm một đoạn dài nhất gồm các phần tử liên tiếp trong dãy A có thứ tự không giảm
Quy ước: Đoạn chỉ gồm đúng 1 phần tử trong A cũng được coi là có thứ tự không giảm
Input
Dòng 1 chứa số nguyên dương n ≤ 105
Dòng 2 chứa n số nguyên a1, a2, ..., an ("i: |ai| ≤ 109) cách nhau ít nhất một dấu cách
Output
Một số nguyên duy nhất là số phần tử trong đoạn tìm được
Example:
Input
11
88 99 11 22 22 33 11 66 33 44 77
Output:
4

  • Solution:

Vì dãy tăng liên tiếp nên ta có dãy arr2[] (chiều dài max) quy hoạch sau:
Với i:= 1->n-1; arr2[0]=1;
- arr1[i]>=arr[i-1] thì arr2[i] = arr2[i-1]+1;
arr1[i]< arr[i-1] thì arr2[i] = 1;
Với test mẫu trên ta thu được:
arr2[i:=0->n-1] = {1, 2, 1, 2, 3, 4, 1, 2, 1, 2, 3};


Có nghĩa là arr2[i] lưu lại số lượng phần tử tại đó mà dãy đang tăng. Sau đó tìm max (arr2[i:=0->n-1])

  • Code:

C++:

https://ideone.com/qfIljk

#include <iostream>
using namespace std;
 
int main ()
{
    int n;
    cin>>n;
    long arr[100005];
    for (int i=0; i<n; i++)
    {
        cin>>arr[i];
    }
    int arr2[100005];
    arr2[0] = 1;
    for (int i=1; i<n; i++)
    {
        if (arr[i]>=arr[i-1])
            arr2[i] = arr2[i-1]+1;
        else
            arr2[i]=1;
    }
    int Dmax = 0;
    for (int i=0; i<n; i++)
    {
        if (arr2[i]>Dmax) Dmax = arr2[i];
    }
    cout<<Dmax;
    return 0;
} 




JAVA:


BCCHIANHOM - Chia nhóm

BCCHIANHOM - Chia nhóm

Link Sub: http://www.spoj.com/PTIT/problems/BCCHIANHOM/
Người Gửi: Default

  • Problem:

Cho dãy số A gồm N số (N <= 10) a1, a2, .., an. và một số nguyên dương K(1 < K < N). Hãy đưa ra số cách để chặt dãy số thành K nhóm (các phần tử trong nhóm là liên tiếp) mà các nhóm có tổng bằng nhau.
Input
Dòng đâu tiên bao gồm 2 số nguyên n và k (0 < n <= 12, 0 < k <= n)
Dòng tiếp theo bao gồm n số nguyên a1, a2, …, an (-10000 <= ai <= 10000)
Output
In ra số cách thỏa mãn.
Example:
Input
3 2
-2 0 -2
Output:
2

Input
3 2
1 2 3
Output:
1
  • Solution:

- Sử dụng QHĐ cơ bản: lưu tổng giá trị của mảng trước đó tại mỗi phần tử (arr[i]=arr[i-1]+value) VD: i:1-3 {-2 0 -2} -> arr[]={0 -2 -2 -4} (i:0->3) - Đệ quy - quay lui: + Vì dãy chặt tạo thành các nhóm và tổng số phần tử các nhóm luôn = n -> Nên với mỗi arr[i] sẽ là trường hợp tổng giả sử có thể chặt được. và ta có vt=i; + Với mỗi tổng giả sử có được, ta lặp lại hành động với dãy arr[vt+1->n]; (p/s tổng có từ quy hoạch trên ta có SUM_[i->j] = arr[j]-arr[i-1]).

  • Code:

C++:



JAVA:


BCTEST15 - Quân mã

Link Sub: http://www.spoj.com/PTIT/problems/BCTEST15/
Người Gửi: Dương Lee

  • Problem:

Trên bàn cờ kích thước  ô, gồm m dòng, n cột. Các dòng được đánh số từ 1..m, từ trên xuống dưới, các cột được đánh số từ 1..n từ trái qua phải, mỗi ô ghi một số nguyên dương. Một quân mã đứng trên một ô của dòng 1 cần phải nhảy đến một ô nào đó của dòng m theo quy tắc đi của quân mã trên bàn cờ Quốc tế và chỉ được nhảy từ dòng có chỉ số bé đến dòng có chỉ số lớn hơn.  
Quy tắc đi quân mã trên bàn cở quốc tế: Từ ô có hình quân mã, nó có thể nhảy tới các ô có chấm đỏ.
Yêu cầu: Tìm cách nhảy sao cho tổng các số ghi trên các ô mà quân mã nhảy qua là lớn nhất (kể cả ô đầu tiên mà quân mã đứng).
Input
-          Dòng đầu ghi 2 số m, n (1≤m,n≤100) cách nhau bởi dấu cách.  
-          m dòng sau mỗi dòng ghi n số nguyên dương không quá 10000, các số cách nhau 1 dấu cách.
Output
Một số duy nhất là tổng lớn nhất của các số ghi trên các ô mà quân mã nhảy qua.
Example:
Input
3 5
9 4 5 6 7
3 6 8 9 1
9 6 2 8 3
Output:
26

  • Solution:

Giải thích: Quân mã đi theo các ô có số được gạch chân. 9 -> 8-> 9.
Ý tưởng: sumM[i][j] = max (chess[i][j], chess[i][j]+max(sumM[i-2][j-1], sumM[i-1][j-2], ... )); Vì quân mã chỉ đi từ hàng chị số thấp đến cao vậy nên ta có thể duyệt xuôi và lưu lại giá trị max có thể có của nó. Dễ hiểu là: Tại mỗi vị trí thì max tại đó sẽ là max của bước cha + chính nó. VD: chess[3][5]: 9 4 5 6 7 3 6 8 9 1 9 6 2 8 3 -> sumM[3][5]: 9 4 5 6 7 8 12 17 13 26 23 15 20 20 -> Max: 26

  • Code:

C++:



JAVA:


GOODFRIE - Good friends

GOODFRIE - Good friends

Link Sub: http://www.spoj.com/PTIT/problems/GOODFRIE/
Người Gửi: Dương Lee

  • Problem:

Trong một lớp học, cô giáo xếp hạng N học sinh theo thứ tự điểm số từ cao xuống thấp. Hai học sinh sẽ là bạn nếu thứ tự của họ là gần nhau, tức là khác biệt giữa thứ tự không quá K. Ví dụ: nếu K = 1, thì chỉ có 2 học sinh ở trước và sau danh sách là bạn của 1 học sinh. Thêm nữa, hai học sinh gọi là bạn tốt nếu họ là bạn và tên của họ có cùng độ dài.  
Viết chương trình tính số các cặp bạn tốt trong lớp.
Input
Dòng đầu chứ N (3<=N<=300 000) và K (1<= K <=N).  
N dòng tiếp theo, mỗi dòng chứa tên một học sinh theo danh sách xếp hạng (từ 2 đến 20 chữ cái tiếng Anh in hoa).
Output
Số cặp bạn tốt.
Example:
Input
4 2
IVA
IVO
ANA
TOM
Output:
5

Input
6 3
CYNTHIA
LLOYD
STEVIE
KEVIN
MALCOLM
DABNEY
Output:
2
  • Solution:

Quy hoạch mảng arr có dạng: tất cả độ dài của xâu_i có arr[i] = tất cả độ dài của xâu_(i-1) arr[i-1] + 1 (của độ dài xâu_i). VD: 3 IV IVO AN -> arr[0] = {at[0]=0, at[1]=0, at[2]=0,...}; arr[1] = arr[0] + arr[0]{at[2]++} = {at[0]=0, arr[1]=0, arr[2]=1, arr[3]=0,...}; arr[2] = arr[1] + arr[1]{at[3]++} = {at[0]=0, arr[1]=0, arr[2]=1, arr[3]=1,...}; arr[3] = arr[2] + arr[2]{at[2]++} = {at[0]=0, arr[1]=0, arr[2]=2, arr[3]=1,...}; -> Như vậy: arr[i].at[j] lưu lại số lượng xâu có độ dài là j từ xâu: 1->i; -> Tại mỗi xâu ta có thể xác định số cặp có thể ghép được với xâu đó bằng phép tính: (arr[i].at[len] - arr[i-k-1].at[len] - 1);

  • Code:

C++:



JAVA: