Hiển thị các bài đăng có nhãn Phát Triển. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Phát Triển. Hiển thị tất cả bài đăng
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:

...

PTIT017K - ACM PTIT 2017 K - QUÂN MÃ

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

  • Problem:

Xét lưới ô vuông vô hạn trong đó có một số ô cấm, các ô còn lại là tự do. Các dòng và cột của lưới được đánh số theo thứ tự bởi các số nguyên … -3 -2 -1 0 1 2 3 … Các cột được đánh số theo thứ tự từ trái sang phải, còn các dòng theo thứ tự từ dưới lên trên. Ô nằm trên giao của dòng x và cột y được gọi là ô (x, y). Một quân mã  đặt ở ô xuất phát là ô (0,0). Sau một bước đi, ta có thể di quân mã đến một trong các ô ở đỉnh đối diện trên đường chéo của hình chữ nhật kích thước 2×3.









  X

  X 



  X


  
  X




  M




  X



  X



  X

  X










Luật di chuyển của quân mã
Yêu cầu: Cho biết toạ độ của các ô cấm, vị trí ô đích nơi quân mã cần đến, hãy tìm cách di chuyển quân mã từ ô (0,0) đến ô đích sao cho số lượng bước đi cần thực hiện là ít nhất.
Input
Dòng đầu tiên chứa T (T ≤ 3) là số lượng test, tiếp đến là T nhóm dòng, mỗi nhóm chứa dữ liệu về một test theo khuôn dạng sau:
 Dòng đầu tiên chứa 2 số nguyên xt, yt được ghi cách nhau bởi dấu cách cho biết toạ độ của ô đích là (xt, yt); 
 Dòng thứ hai chứa số nguyên dương n (n ≤ 1000) là số lượng ô cấm;
 Dòng thứ i trong số n dòng tiếp theo chứa hai số nguyên được ghi cách nhau bởi dấu cách xi, yi cho biết (xi, yi) là toạ độ của ô cấm thứ i (i = 1, 2, …, n).
Output
Gồm T dòng mỗi dòng chứa kết quả của một test tương ứng trong dữ liệu vào là số lượng bước đi ít nhất cần thực hiện để di chuyển quân mã từ ô xuất phát (0,0) đến ô đích. Ghi số −1 nếu như không thể di chuyển quân mã đến ô đích.
Example:
Input
1
2 4
0
Output:
2

  • Solution:

- Bài này cho lưới ô vuông vô hạn nhưng thực ra tối đa chỉ có nằm trong khoảng -1000->1000, và max khoảng tầm 1000 điểm cấm. Nên căng lắm lưới cỡ 10^5 là có thể tìm thấy đường đi đến điểm cần tìm rồi.
- Nhưng các điểm còn có cả tọa độ âm nên khó cho việc biểu diễn trên máy. Nên mình quy về các đỉnh, mỗi ô là đại diện cho một đỉnh.

Công thức chuyển: "(2*y_max*y_max + 3*y_max - x_max + 1) + (2*y_max + 1)*y + x". Công thức này thực ra chỉ là tính diện tích thôi, các bạn có thể tự thiết lập.
- Nhưng vấn đề lại đặt ra đó là: (10^5*10^5+1)^2 đỉnh thì không một cấu trúc tuần tự nào (arr, vector) chứa nổi -> Mình quyết định chuyển sang "map" có cấu trúc liên kết để giải quyết vần đề này.
- Cuối cùng là chỉ việc BFS kết hợp với map nữa là ok.

  • Code:

C++:

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

const long long x_max = 100000;
const long long y_max = 100000;
#define ll long long

int inSide (ll x, ll y)
{
    if (x>=-x_max && x<=x_max && y>=-y_max && y<=y_max)
        return 1;
    return 0;
}

ll vertex (ll x, ll y)
{
    return (2*y_max*y_max + 3*y_max - x_max + 1) + (2*y_max + 1)*y + x;
}

ll x_t, y_t;
ll n;
ll x_i, y_i;
map <ll, char> prevent;
void read ()
{
    scanf ("%lld%lld", &x_t, &y_t);
    scanf ("%lld", &n);
    for (ll i=0; i<n; i++)
    {
        scanf ("%lld%lld", &x_i, &y_i);
        prevent[vertex(x_i, y_i)] = 'X';
    }
}

map <ll, char> check;
map <ll, ll> len;
void init ()
{
    prevent.clear();
    check.clear();
    len.clear();
}

struct data
{
    ll x, y;
    data (ll x_, ll y_)
    {
        x = x_;
        y = y_;
    }
};

ll x_xq[] = {-1, -2, -2, -1, +1, +2, +2, +1};
ll y_xq[] = {-2, -1, +1, +2, +2, +1, -1, -2};

void BFS (ll x, ll y)
{
    queue <data> q;
    q.push(data(x, y));
    check[vertex(x, y)] = 'Y';
    len[vertex(x, y)] = 0;
    
    while (!q.empty())
    {
        data v = q.front();
        q.pop();
        for (int i=0; i<8; i++)
        {
            ll x_p = v.x+x_xq[i];
            ll y_p = v.y+y_xq[i];
            if (inSide(x_p, y_p)==1 && check[vertex(x_p, y_p)]!='Y' && prevent[vertex(x_p, y_p)]!='X')
            {
                q.push(data(x_p, y_p));
                len[vertex(x_p, y_p)] = len[vertex(v.x, v.y)]+1;
                check[vertex(x_p, y_p)] = 'Y';
                if (x_p == x_t && y_p == y_t) return;
            }
        }
    }
}

int main ()
{
    int t;
    scanf ("%d", &t);
    while (1)
    {
        if (t==0) break;
        t--;
        init();
        read();
        BFS(0, 0);
        printf("%lld\n", len[vertex(x_t, y_t)]);
    }
    return 0;
}

JAVA:

...

Python:

...
BCTEST14 - Ốc sên

BCTEST14 - Ốc sên

Người Gửi: Darkness

  • Problem:

Có một con ốc sên dưới mặt đất. Nó muốn leo lên đỉnh một cái cột bằng gỗ với chiều cao V mét, tính từ mặt đất. Trong một ngày, nó có thể leo lên A mét, tuy nhiên, trong mỗi đêm nó ngủ, nó lại bị tụt xuống B mét. Xác định số ngày cần thiết để con ốc sên leo tới đỉnh.


Input

Ba số nguyên cách nhau bởi dấu cách: A,B và V (1≤B<A≤V≤1 000 000 000).
Output
Một số nguyên duy nhất là số ngày cần thiết để con ốc sên leo tới đỉnh
Example:
Input
2 1 5
Output:
4

Input
5 1 6
Output:
2
Input
100 99 1000000000
Output:
999999901
  • Solution:

Bài này ta thấy A-B là chênh lệch mỗi ngày của con ốc sên di chuyển nên ta áp dụng như sau:
- Gọi: V-B là độ cao cần thiết để con ốc sên cần x ngày để lên tới đỉnh:
+ Nếu (V-B) chia hết cho (A-B): Thì con ốc sên đó cần x = (V-B)/(A-B) ngày để đạt được V-B độ cao. Và ngay sáng hôm sau con ốc sên lên A mét nữa sẽ lên tới đỉnh. (Luôn đúng vì A>B -> V-B+A>V).
+ Nếu không chia hết thì: x = [(V-B)/(A-B)]+1 (Cần thêm 1 ngày nữa)

  • Code:


#include <iostream>
using namespace std;
 
int main ()
{
    long A, B, V;
    cin>>A>>B>>V;
    if (A==V) cout<<"1";
    else
    {
        if ((V-B)%(A-B)==0) cout<<(V-B)/(A-B);
        else cout<<(V-B)/(A-B)+1;
    }
    return 0;
}



JAVA:


P151PROG - ROUND 1G - Xếp hàng

P151PROG - ROUND 1G - Xếp hàng

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

  • Problem:

Trong giờ ăn trưa tại Học viên Công nghệ Bưu chính Viễn thông, có n sinh viên đang xếp hang để lấy đồ.  
Cảm thấy chán vì phải đứng đợi một mình, vì vậy mỗi sinh viên viết ra mã sinh viên của mình đứng ngay trước và ngay sau của mình. Nếu không có ai đứng trước hoặc không có ai đứng sau thì viết ra 0.  
Đột nhiên, xe chở nước sôi đi qua, tất cả sinh viên phải tránh. Khi họ trở lại, họ không nhớ vị trí của mình mà chỉ nhớ mã sinh viên của người đừn trước và người đứng sau.  
Hãy giúp các sinh viên PTIT tìm lại vị trí của mình!!!!
Input
Dòng đầu tiên gồm số tự nhiên n (2 ≤ n ≤ 2·10^5) – số lượng sinh viên.
n dòng tiếp theo, dòng thứ i gồm cặp số tự nhiên ai, bi (0 ≤ ai, bi ≤ 10^6), với ai là mã sinh viên của người đứng trước, bi là mã sinh viên của người đứng sau 1 sinh viên nào đó. Nếu không có ai đứng trước hoặc không có ai đứng sau nhập 0.
Mã sinh viên của mỗi sinh viên là khác nhau. 
Output
Trên 1 dòng, in ra n số  x1, x2, ..., xn  , danh sách của các sinh viên theo thứ tự ban đầu.
Example:
Input
4
92 31
0 7
31 0
7 141
Output:
92 7 31 141

  • Solution:

Bài này sẽ có hai trường hợp chính: - n lẻ, dãy VD: 7 8 6 biểu diễn input là: 0 8 7 6 8 0 -> Sử dụng mảng đánh dấu ta lưu lại được 2 dãy liên kết: + arr[0] = 8, arr[8] = 0 -> truy vết thu được: D1: 8 (0 không tính vào dãy) + arr[6] = 7, arr[7] = -1 -> truy vết thu được: D2: 6 7 (Ở đây tìm được 6 là số cuỗi dãy 2 vì sau 6 không còn số nào liên kết với nó) -> In D2 và D1 xen kẽ: 7 8 6 (D1 in thuận, D2 in ngược). - n chẵn, dãy VD: 7 8 6 1 biểu diễn input là: 0 8 7 6 8 1 6 0 -> Sử dụng mảng đánh dấu ta lưu lại được 2 dãy liên kết: + arr[0] = 8, arr[8] = 1, arr[1]=-1 (=-1 là khởi tạo để kết thúc kết nối) -> truy vết thu được: D1: 8 1 + arr[0] = 6, arr[6] = 7, arr[7]=-1 (=-1 là khởi tạo để kết thúc kết nối) -> truy vết thu được: D2: 6 7 -> In D2 và D1 xen kẽ: 7 8 6 1 (D1 in thuận, D2 in ngược).

  • Code:

C++:

https://ideone.com/HVFFEX

#include <iostream>
#include <vector>
using namespace std;

int SV_sau[1000006];
int SV_truoc[1000006];
void init ()
{
    for (int i=0; i<=1000000; i++)
    {
        SV_sau[i]=-1;
        SV_truoc[i]=-1;
    }
}

vector <int> so;
int dd[1000006] = {0};
void check (int x)
{
    if (dd[x]==0 && x!=0)
    {
        so.push_back(x);
        dd[x]=1;
    }
}

int main ()
{
    init ();
    int n;
    cin>>n;
    int vt1, vt2;
    for (int i=1; i<=n; i++)
    {
        int a, b;
        cin>>a>>b;
        check (a); check (b);
        SV_sau[a] = b;
        SV_truoc[b] = a;
    }
    vector <int> D1;
    vector <int> D2;
    if (n%2==0)
    {
    //Day 1
        int st = 0;
        while (1)
        {
            if (SV_sau[st]==-1) break;
            D1.push_back(SV_sau[st]);
            st = SV_sau[st];
        }
        // Day 2
        st = 0;
        while (1)
        {
            if (SV_truoc[st]==-1) break;
            D2.push_back(SV_truoc[st]);
            st = SV_truoc[st];
        }
        //IN
        int D_1 = 0, D_2 = D2.size()-1;
        for (int i=1; i<=n; i++)
        {
            if (i%2!=0)
            {
                cout<<D2[D_2]<<" ";
                D_2--;
            }
            else
            {
                cout<<D1[D_1]<<" ";
                D_1++;
            }
        }
    }
    else
    {
        //Day 1
        int st = 0;
        while (1)
        {
            if (SV_sau[st]==0) break;
            D1.push_back(SV_sau[st]);
            st = SV_sau[st];
        }
        st=0;
        for (int i=0; i<so.size(); i++)
        {
            if (SV_sau[so[i]]==-1)
            {
                st=so[i];
                break;
            }
        }
        if (st!=0) D2.push_back(st);
        while (1)
        {
            if (SV_truoc[st]==-1) break;
            D2.push_back(SV_truoc[st]);
            st = SV_truoc[st];
        }
        int D_1 = 0, D_2 = D2.size()-1;
        for (int i=1; i<=n; i++)
        {
            if (i%2!=0)
            {
                cout<<D2[D_2]<<" ";
                D_2--;
            }
            else
            {
                cout<<D1[D_1]<<" ";
                D_1++;
            }
        }
    }
    return 0;
}



JAVA:


PTIT017A - ACM PTIT 2017 A - ƯỚC SỐ NGUYÊN TỐ

PTIT017A - ACM PTIT 2017 A - ƯỚC SỐ NGUYÊN TỐ

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

  • Problem:

Cho các số nguyên A, B, K. Nhiệm vụ của bạn là hãy đếm xem trong đoạn [A, B] có bao nhiêu số có đúng K ước số nguyên tố.  
Ví dụ số 12 có 2 ước số nguyên tố (2 và 3), số 550 có 3 ước số nguyên tố (2, 5, và 11), trong khi số 17 chỉ có 1 ước số nguyên tố duy nhất là chính nó.
Input
Dòng đầu tiên là số lượng bộ test T (T ≤ 100).
Mỗi test gồm 3 số nguyên A, B, K (2 ≤ A, B ≤ 107, 1 ≤ K ≤ 109)
Output
Với mỗi test in ra chữ “Case” kèm theo số thứ tự bộ test và đáp án tìm được (theo mẫu như trong ví dụ).
Example:
Input
5
5 15 2
2 10 1
24 42 3
1000000 1000000 1
1000000 1000000 2
Output:
Case #1: 5
Case #2: 7
Case #3: 2
Case #4: 0
Case #5: 1

  • Solution:

Chú Ý: Code dưới đây sub bằng C++6.3: time limit, C++4.3.2: 0.65s ??? Ý tưởng: - Sàng nguyên tố trong khoảng từ 1->10^7. (arr[]) - Mỗi nguyên tố xét duyệt các bội của nó. Bội của các số nguyên tố sẽ có ước là số nguyên tố đó -> dùng mảng đánh dấu (arr2[]) ghi nhận lại số ước số nguyên tố của số đó. VD: 1->5 -> (arr[1]=0, arr[2]=1, arr[3]=1, arr[4]=0, arr[5]=1); -> v[]={2, 3, 5} là các số nguyên tố. với v[0]=2 -> arr2[2]= 1, arr2[3]= 0, arr2[4]= 1, arr2[5]= 0; với v[1]=3 -> arr2[2]= 1, arr2[3]= 1, arr2[4]= 1, arr2[5]= 0; với v[3]=5 -> arr2[2]= 1, arr2[3]= 1, arr2[4]= 1, arr2[5]= 1; -> Vậy arr2[i] tương đương với số ước nguyên tố của i;

  • Code:

C++:

https://ideone.com/HyEtOE

#include <iostream>
#include <math.h>
#include <stdio.h>
#include <vector>
#define MAX 10000000
using namespace std;


long arr[10000007];
long arr2[10000007];
void init ()
{
    for (long i=1; i<=MAX; i++)
    {
        arr[i]=1;
        arr2[i]=0;
    }
    arr[1]=0;
}

void sangNT ()
{
    for (long i=2; i<=sqrt(MAX); i++)
    {
        if (arr[i]==1)
        {
            for (long j=2; j<=MAX/i; j++)
            { 
                arr[j*i]=0;
            }
        }
    }
}

int main ()
{
    init ();
    sangNT ();
    vector <long> v;
    for (long i=1; i<=MAX; i++)
    {
        if (arr[i]==1) v.push_back(i);
    }
    for (long i=0; i<v.size(); i++)
    {
        for (long j=1; j<=MAX/v[i]; j++)
        {
            arr2[v[i]*j]++;
        }
    }
    int t;
    cin>>t;
    for (int k=1; k<=t; k++)
    {
        long A, B, K, count=0;
        cin>>A>>B>>K;
        for (long i=A; i<=B; i++)
        {
            if (arr2[i]==K) count++;
        }
        printf ("Case #%d: %ld\n", k, count);
    }
    
    return 0;
}


JAVA:


PTIT017J - ACM PTIT 2017 J - SỐ CÁC SỐ KHÔNG CHIA HẾT

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

  • Problem:

Cho 5 số tự nhiên a, b, c, d, e là các số nguyên tố cùng nhau từng đôi một. Hãy cho biết có bao nhiêu số nhỏ hơn hoặc bằng N không chia hết cho bất kỳ số nào trong các số a, b, c, d, e (N<263
Input
Dòng đầu tiên là số lượng bộ test T (T ≤ 50). 
Mỗi test gồm hai dòng:  dòng đầu tiên ghi lại số N, dòng kế tiếp ghi lại 5 số a, b, c, d, e nguyên tố cùng nhau (cả 5 số đều không quá 1000).
Output
Ứng với mỗi test đưa ra số lượng các số không chia hết cho bất kể số nào trong các số a, b, c, d, e
Example:
Input
3
1000
2 3 5 7 11
10000
3 4 5 7 11
1000000000
9 11 13 17 19
Output:
207
3116
665093420

  • Solution:

- Bài này dựa vào toán rời rạc 1 để làm VD: Số lượng các số chia hết cho 2 (<= N) là: [N/2] (trong đó [] là chia lấy nguyên). Số lượng các số chia hết cho 3 (<= N) là: [N/3] (trong đó [] là chia lấy nguyên). Số lượng các số chia hết cho cả 2 và 3 (<= N) là: [N/6] (trong đó [] là chia lấy nguyên). -> Số lượng các số không chia chia hết cho 2 và 3 (<= N) là: N - ([N/2] + [N/3] - [N/6]) (trong đó [] là chia lấy nguyên). Tổng quát: Với 5 số A, B, C, D, E thì: ta có đáp án: = N - ([N/A]+[N/B]+..+[N/E] -[N/(A*B)]-[N/(A*C)]-...-N[N/(D*E)] +[N/(A*B*C)]+[N/(A*B*D)]+...+[N/(C*D*E)]) -[N/(A*B*C*D)]-...-[N/(B*C*D*E)] +[N/(A*B*C*D*E)]) Ví dụ cho 3 số A=2 B=3 C=5, biểu diễn các số chia hết cho A, B, C theo dạng tập hợp;
Ta thấy S(A+B+C) là tập những số chia hết cho 2 hoặc 3 hoặc 5 = S(A) + S(B) + S(C) - S(A.B) - S(B.C) - S(C.A) + S(A.B.C)
trong đó S (x) là tập các số chia hết cho x. 
Việc cần làm giờ là chỉ cần sinh nhị phân chọn các cặp số và tính toán số lượng các số của các cặp tích số.

  • Code:

C++:



JAVA:


P173SUMI - ROUND 3I - Chia pho mát

P173SUMI - ROUND 3I - Chia pho mát

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

  • Problem:

Kled và Skaar tìm thấy hai miếng pho mát trong rừng có khối lượng a và b gam. Nhưng cả 2 đều tham và muốn ăn miếng to hơn. Đúng lúc đó Gragas đến và đưa ra một đề nghị sẽ làm cho khối lượng 2 miếng như nhau để công bằng. Kled và Skaar tò mò hỏi : “Bạn sẽ làm thế nào?”. Gragas nói : “Thật dễ dàng. Nếu khối lượng miếng nào chia hết cho 2, tôi sẽ ăn một nửa miếng đó. Nếu khối lượng miếng nào chia hết cho 3, tôi sẽ ăn 2/3 miếng đó và nếu khối lượng miếng nào chia hết cho 5, tôi sẽ ăn 4/5 miếng đó. Tôi sẽ ăn một ít ở từng miếng và sẽ làm cho chúng bằng nhau”.  
Kled và Skaar thấy khá hợp lí và đồng ý với đề nghị của Gragas. Tuy nhiên họ đề nghị Gragas cần làm cho 2 miếng pho mát bằng nhau càng nhanh càng tốt. Tìm số lượng tối thiểu lần ăn của Gragas để làm 2 miếng pho mát bằng nhau.
Input
Dòng duy nhất chứa hai số nguyên a và (1 ≤ a, b ≤ 109).
Output
In ra đáp án của bài toán. Nếu không có cách nào làm cho 2 miếng pho mát bằng nhau, in ra -1. Nếu ban đầu hai miếng đã bằng nhau, in ra 0.
Example:
Input
15 20
Output:
3

Input
14 8
Output:
-1
  • Solution:

Bài này nhận thấy ăn hai miếng pho mát a và b là cách ăn dùng phép chia. -> Vậy để hai miếng pho mát còn lại sau khi ăn là p1 và p2 bằng nhau thì hai miếng pho mát p2=p1 đó phải là ước chung của a và b. -> B1: Phân tích ước của a và b. Và duyệt từng ước chung của a và b là p2=p1; -> B2: Sau khi có ước chung là p1=p2 ta sẽ có phần còn lại cần phải ăn là: p1c=b/p1 và p2c=a/p2; -> B3: Ăn hết p1c và p2c theo cách ăn của đề bài, với mỗi lần ăn thì kết hợp luôn cả đếm số lần ăn. -> B4: Nếu p1c và p2c sau khi ăn theo cách trên mà chỉ còn lại = 1 thì ước chung p2=p1 trên là ăn được và sẽ so sánh với min_bước-ăn trước đó. Nếu != 1 thì có nghĩa là ước chung đó không ăn được.

  • Code:

C++:



JAVA:


PTIT122G - Số đối xứng 2

PTIT122G - Số đối xứng 2

Người Gửi: Sai

  • Problem:

Cho một số nguyên N (có thể chứa các số 0 ở đầu). Hỏi phải cộng thêm tối thiểu bao nhiêu để N trở thành số đối xứng (số cộng thêm là một số nguyên không âm - khi cộng tính cả các số 0 ở đầu nhé).
Input
Gồm nhiều bộ test, mỗi bộ test gồm một dòng chứa số nguyên N – có từ 2 đến 9 chữ số, không có dấu cách, có thể có các số 0 ở đầu.  
Dữ liệu vào kết thúc bởi dòng chứa số 0.
Output
Mỗi bộ test in trên một dòng chứa kết quả của bộ test.
Example:
Input
100000
100001
00456
000121
0
Output:
1
0
979
44

  • Solution:


- Phương án tốt nhất là:
 + Với mỗi cặp số tại i và n-1-i (n là length xâu) (VD: 12345 có các cặp số là 1-5, 2-4, 3-3)
 + Nếu mỗi cặp có [i] = [n-1-i] thì không cần xét đến vì nó đã đối xứng rồi. (VD: 1405231 thì cặp 1-1 không phải xét nữa).
 + Nếu cặp [i] > [n-1-i] thì chỉ cần chuyển [n-1-i]=[i] (VD: 1405231 -> 1415241 : cặp 4-3 -> 4-4)
+ Nếu cặp [i] < [n-1-i] thỉ chuyển [n-1-i]=[i] và tăng tiếp các số [n-1-i-j] thêm 1 nếu còn nhớ 1 (j:(1->i)) (VD: 1415241 -> 1416141 : cặp 1-2 -> 1-1 và 5 thêm 1->6)
  *Tại sao lại tăng thêm 1? Vì để chuyển 2->1 thì số phải chạy qua 10, có nghĩa là 2+9=11 ->lấy 11/10 -> 1 nên phải tăng thêm vào số kề trước nó để đúng quy tắc cộng số (vì đây là cộng số tạo đối xứng vậy nên nếu không tăng thêm theo quy tắc cộng số thì số tạo ra có thể < số ban đầu).
 *Tại sao lại tăng thêm 1 nếu còn nhớ 1? VD: 1992 -> 1991 (nhớ 1) -> 2001 -> 2002; => Nếu chỉ nhớ 1 lần thì chỉ tạo ra 1992 -> 1901. Vậy nên nếu cộng vào số tiếp theo mà vẫn còn tiếp tục nhớ 1 thì phải tiếp tục cộng thêm 1 cho số tiếp theo cho đến khi nhớ 0.
1410141 -> 1410141
Quá trình lặp lại cho đến khi tạo được số đối xứng. VD: 1409231 -> 1409241 1409241 -> 1409041 (nhớ 1) -> 1410041 1410041 -> 1410141
 

  • Code:

C++:



JAVA:


BCTEST13 - Tổng may mắn

BCTEST13 - Tổng may mắn

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

  • Problem:

Cùng sở thích với Bờm, Tèo cũng rất thích các số may mắn. Ta đã biết rằng một số gọi là số may mắn nếu biểu diễn thập phân của nó chỉ chứa các chữ số may mắn là 4 và 7. Ví dụ: Các số 47,744,4 là số may mắn còn 5,17,467 không phải là số may mắn.  
Nhưng khác với Bờm, Tèo lại định nghĩa next(x) là số may mắn bé nhất lớn hơn hoặc bằng x.  
Mở rộng hơn nữa, Tèo lại nghĩ ra Tổng may mắn LKSUM của hai số l,r (l≤r) như sau:  
LKSUM=next(l)+next(l+1)+...+next(r-1)+next(r)  
Bài toán đã trở nên khó khăn hơn nên Tèo cần sự giúp đỡ của các bạn sinh viên PTIT. Hãy giúp Tèo bài toán trên nhé !!!

Input
Một dòng duy nhất chứa hai số nguyên l và r (1≤l≤r≤109).
Output
Một số nguyên duy nhất là giá trị của tổng LKSUM.
Example:
Input
2 7
Output:
33

Giải thích: next(2)+next(3)+next(4)+next(5)+next(6)+next(7)=4+4+4+7+7+7=33
Input
7 7
Output:
7
Giải thích: next(7)=7
  • Solution:

- Cách làm: + Sinh các trường hợp từ 1->10 chữ số cho 4 và 7; Sau khi sinh sẽ có mảng dãy số v[]={ 4, 7, 44, 47, ..., 77..7 } được đánh số thứ tự từ 0, 1, 2, 3, ...., size(). + Chặt nhị phân tìm số lớn hơn gần với nó nhất trong mảng v. (p/s vì sinh tối đa 10 chữ số nên độ phức tạp cũng không nhiều khoảng O(10000) nên có thể dùng tìm kiếm tuyến tính). + Xét các trường hợp và tính toán các khoảng tạo ra. VD: 2 và 8 -> tìm được: 4 và 44 -> tạo ra các khoảng: (2, 4] gồm các số có next()=4; (4, 7] gồm các số có next()=7; (7, 8] gồm các số có next()=44; Như vậy chỉ cần biết được các khoảng chia sẽ biết được có bao nhiêu số trong đó có cùng giá trị next.

  • Code:

C++:



JAVA:


P145PROI - ROUND 5I - Mật khẩu

P145PROI - ROUND 5I - Mật khẩu

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

  • Problem:

Một xâu ký tự được gọi là mật khẩu “an toàn” nếu xâu có độ dài ít nhất bằng 6 và xâu chứa ít nhất một chữ cái in hoa , một chữ cái thường , một chữ số .  
Ví dụ, ‘a1B2C3’, ‘tinHoc6’ là hai mật khẩu “an toàn”. Còn ‘a1B2C’, ’a1b2c3’, ‘A1B2C3’, ‘tinHoc’ đều không phải là mật khẩu “an toàn”.
Một lần, Tí nhìn thấy một xâu S, chỉ gồm các loại ký tự: chữ cái in hoa, chữ cái thường và chữ số. Tí muốn tự kiểm tra khả năng đoán nhận mật khẩu bằng cách đếm xem có bao nhiêu cặp chỉ số (i, j) thỏa mãn điều kiện:  1 ≤ i < j ≤ length(S) và xâu con gồm các ký tự liên tiếp từ i đến j của S là mật khẩu “an toàn”.  
Cho xâu S, các hãy tính số lượng cặp chỉ số (i, j) thỏa mãn điều kiện nêu trên.
Input
Một dòng chứa xâu S có độ dài <= 10^6.
Output
In ra một số nguyên duy nhất là số cặp chỉ số (i, j) tìm được.
Example:
Input
abc3456789PQ
Output:
6

Input
abc123
Output:
0
  • Solution:

- Quy hoạch xâu: Xây dựng mảng pass mà: + Với mỗi một vị trí xác định xem từ đầu xâu cho đến nó có bao nhiêu kí tự số, có bao nhiêu chữ thường, chữ hoa. - Tìm kiếm nhị phân: + Tại mỗi vị trí trên xâu, tìm vị trí gần nhất (VT) mà tại đó mật khẩu an toàn (check_safe) trên mảng pass mới quy hoạch. - Tính toán: + Với mỗi vị trí tìm thấy thì số lượng mật khẩu an toàn tạo được sẽ là +=(n-VT+1) (Vì khi mật khẩu đã an toàn rồi thì thêm bất kì kí tự nào nó vẫn an toàn :v) Độ phức tạp O(NlogN).

  • Code:

C++:

https://ideone.com/LmEflP
#include <iostream>
#include <string>
using namespace std;

struct data
{
    int hv_1;
    int hv_A;
    int hv_a;
} typedef data;

data pass[1000006];

int check_safe (int begin, int end)
{
    int num_1=pass[end].hv_1-pass[begin-1].hv_1;
    int num_a=pass[end].hv_a-pass[begin-1].hv_a;
    int num_A=pass[end].hv_A-pass[begin-1].hv_A;
    if (num_1>0 && num_a>0 && num_A>0 && end-begin+1>=6) return 1;
    else return 0;
}

int BSearch (int begin, int front, int back)
{
    int vt=-1;
    while (front<=back && back-begin+1>=6)
    {
        int mid = (front+back)/2;
        if (check_safe (begin, mid)==1)
        {
            vt=mid;
            back=mid-1; 
        }
        else front=mid+1;
    }
    return vt;
}

int main ()
{
    string xau;
    cin>>xau;
    pass[0].hv_1=0;
    pass[0].hv_a=0;
    pass[0].hv_A=0;
    int n=xau.length();
    for (int i=1; i<=n; i++)
    {
        if (xau[i-1]>='0' && xau[i-1]<='9')
        {
            pass[i] = pass[i-1];
            pass[i].hv_1++; 
        }
        else if (xau[i-1]>='a' && xau[i-1]<='z')
        {
            pass[i] = pass[i-1];
            pass[i].hv_a++;
        }
        else if (xau[i-1]>='A' && xau[i-1]<='Z')
        {
            pass[i] = pass[i-1];
            pass[i].hv_A++;
        }
    }
    long long count=0;
    for (int i=1; i<=xau.length()-6+1; i++)
    {
        int VT = BSearch (i, i, n);
        if (VT!=-1)
        {
            count+=n-VT+1;
        } else break;
    }
    cout<<count;
    return 0;
}

JAVA:


PTIT125I - Xóa chữ số

PTIT125I - Xóa chữ số

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

  • Problem:

Cho một số có N chữ số. Bạn hãy xóa đi K chữ số để được số còn lại sau khi xóa là lớn nhất có thể.
Input
- Dòng 1: số N và K (1<=K<N<=500 000).  
- Dòng 2: Số có N chữ số, bắt đầu bằng số khác 0.
Output
- Số lớn nhất có thể sau khi xóa K chữ số.
Example:
Input
4 2
1924
Output:
94

  • Solution:

Nhận thấy khi xóa đi mà số vẫn lớn nhất thì mỗi lần xóa một chữ phải tạo ra số lớn nhất. VD: 1924 -> Xóa lần 1 -> 924 max -> Xóa lần 2 -> 94 max. Vậy áp dụng stack để tạo dãy tuyến tính không tăng (khi còn xóa được). Dãy đó luôn là dãy lớn nhất có thể tạo được khi xóa. EX: 170803 (K=3) S: 1 (K=3) S: 7 (K=2 (xóa 1)) S: 7 0 (K=2) S: 8 (K=0 (xóa 7, 0)) S: 8 0 (K=0) S: 8 0 3 (K=0) -> 803 max.

  • Code:

C++:

https://ideone.com/8ZXdyM
#include <iostream>
#include <stack>
#include <vector>
using namespace std;

int main ()
{
    long N, K;
    cin>>N>>K;
    stack <int> S;
    for (long i=1; i<=N; i++)
    {
        char tmp_char;
        cin>>tmp_char;
        int tmp_int = tmp_char - '0';
        if (S.empty())
        {
            S.push(tmp_int);
        }
        else
        {
            while (!S.empty() && tmp_int > S.top() && K>0)
            {
                S.pop();
                K--;
            }
            S.push(tmp_int);
        }
    }
    while (K>0 && !S.empty())
    {
        S.pop();
        K--;
    }
    vector <int> smallest;
    while (!S.empty())
    {
        int tmp=S.top();
        S.pop();
        smallest.push_back(tmp);
    }
    for (long i=smallest.size()-1; i>=0; i--)
        cout<<smallest[i];
    return 0;
}

JAVA: