程序員進階之算法練習(七十二) 題目1 題目2 題目3 題目4 題目5

題目1

題目鏈接
題目大意:
給出一個字符串,由小寫字母組成;
現在Alice和Bob在玩遊戲,輪流從字符串中移除一個子串,Alice先操作;
Alice允許移除偶數長度子串,Bob允許移除奇數長度子串;(也允許不移除)
最終看每個人移除子串的分數總和,字母a是1分,b是2分、、、z是26分;
問最終誰能贏得遊戲,以及勝者領先的分數;

輸入:
第一行,整數𝑡 表示t個樣例 𝑡 (1≤𝑡≤5⋅1e4)
每個樣例一行,字符串𝑠 (1≤|𝑠|≤2⋅1e5)

輸出:
每個樣例一行,勝者和勝者領先的分數;

Examples
input
5
aba
abc
cba
n
codeforces
output
Alice 2
Alice 4
Alice 4
Bob 14
Alice 93

題目解析:
Alice先手,並且可以移除偶數字符串,那麼字符串如果是偶數,Alice會移除所有字符;
如果是奇數,Alice只會留下1個字符串,要麼是最左邊,要麼是左右邊的字符,選擇一個較小值;
Bob後手,只能選擇alice剩下的字符串。

class Solution {
    static const int N = 201010;
    string str;

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> str;
            int sum = 0;
            for (int i = 0; i < str.length(); ++i) {
                sum += str[i] - 'a' + 1;
            }
            if (str.length() % 2) {
                int bob = min(str[0], str[str.length() - 1]) - 'a' + 1;
                int alice = sum - bob;
                cout << (alice > bob ? "Alice" : "Bob") << " " << abs(alice - bob) << endl;
            }
            else {
                cout << "Alice " << sum << endl;
            }
        }
    }
}
ac;

題目2

題目鏈接
題目大意:
給出一個字符串,由小寫字母組成;
如果這個字符串的所有子串都滿足,構成字符串的字符數相差不超過1,則稱這個字符串爲完美字符串,比如說:

現在給出一個字符串,詢問是否爲完美字符串;

輸入:
第一行,整數𝑡 表示t個樣例 𝑡 (1≤𝑡≤2⋅1e4)
每個樣例一行,字符串𝑠 (1≤|𝑠|≤2⋅1e5)

輸出:
每個樣例一行,如果是完美字符串則輸出YES;如果不是完美字符串則輸出NO;

Examples
input
5
aba
abb
abc
aaaaa
abcba
codeforces
output
YES
NO
YES
YES
NO

題目解析:
根據題目的要求,任意子串的字符數相差要在1以內,假設一共有k個不同字符;
那麼從字符串中任意截取k長度的字符串,必然會由不同的字符組成,否則就會出現重複字符數>1,然後沒出現的字符數位0,那麼就不符合題目的要求;
並且由於可以任取,我們在[1, k]是由k個不同的字符構成,[2, k+1]也是k個不同的字符構成,由此可以推導出str[k+1] = str[1],並由此類推,完美字符串必然是abcd abcd abc 這樣的重複構成;
這樣只需要檢測字符串是否滿足這個特性即可。

class Solution {
    static const int N = 201010;
    string str;

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> str;
            int sum = 0, v[26] = {0};
            for (int i = 0; i < str.length(); ++i) {
                int index = str[i] - 'a';
                if (!v[index]) {
                    v[index] = 1;
                    ++sum;
                }
            }
            bool ans = true;
            memset(v, 0, sizeof(v));
            for (int i = 0; i < sum; ++i) {
                int index = str[i] - 'a';
                if (!v[index]) {
                    v[index] = 1;
                }
                else {
                    ans = false;
                    break;
                }
            }
            if (ans) {
                int pos = sum;
                while (pos < str.length() && ans) {
                    for (int i = pos; i < str.length() && i < (pos + sum); ++i) {
                        if (str[i] != str[i - sum]) {
                            ans = 0;
                            break;
                        }
                    }
                    pos += sum;
                }
                if (ans) {
                    cout << "YES" << endl;
                }
            }
            if (!ans) {
                cout << "NO" << endl;
            }
        }
    }
}
ac;

題目3

題目鏈接
題目大意:
給出一個數字n,將數字n可以拆分成若干個整數之和;
現在想知道,有多少種拆分方法,要求拆分出來的整數都是迴文數;
(拆分出來的數字至少有一個不同,纔算不同組合)

輸入:
第一行,整數𝑡 表示t個樣例 𝑡 (1≤𝑡≤1e4)
每個樣例一行,整數 𝑛 (1≤𝑛≤4⋅1e4)

輸出:
每個樣例一行,輸出不同的組合數字;結果可以對1e9+7取模;

Examples
input
2
5
12
output
7
74

題目解析:
首先,把1到40000的迴文數全部列出來,得到若干個迴文數;
題目的要求是計算數字n拆分有多少種組合,我們只看數字1和2,就是將數字n拆成1和2的和;
這個和動態規劃的經典題目類似:上n個臺階,每次有1步或者2步,最後有多少走法;

但是這個題目有點不同,就是對不同走法的判斷,這裏只有新增不同數字的情況,才認爲是不同的;(1+2和2+1是一樣的)
那麼我們將回文數數字從小到大排列,然後判斷每次迴文數是否可以替換已有數字即可。
比如說:
考慮數字1,有dp[1]=1,dp[2]=1, dp[3]=1, dp[4]=1;(dp[i]表示數字i有多少總走法)
考慮數字2,有dp[1]=1,dp[2]=2, dp[3]=2, dp[4]=3;對於dp[2],引入2的時候多了2=2的選擇,同時還有原來的2=1+1;對於dp[4],可以在dp[2]的基礎上+2(新增2種選擇4=2+2, 4=1+1+2),也可以不使用2,保留原來的4=1+1+1+1;

按照這種思路分析,可以得到狀態轉移方程還是dp[i]=dp[i]+dp[i-k];(k是迴文數)

class Solution {
    static const int N = 40100;
    static const int MOD = 1e9 + 7;
    int dp[N];
    
    bool check(int k) {
        vector<int> vec;
        while (k) {
            vec.push_back(k % 10);
            k /= 10;
        }
        for (int i = 0; i < vec.size() / 2; ++i) {
            if (vec[i] != vec[vec.size() - i - 1]) {
                return false;
            }
        }
        return true;
    }

public:
    void solve() {
        vector<int> vec;
        for (int i = 1; i < N; ++i) {
            if (check(i)) {
                vec.push_back(i);
            }
        }
        dp[0] = 1;
        
        for (int j = 0; j < vec.size(); ++j) {
            for (int i = vec[j]; i < N; ++i) {
                dp[i] = ((lld)dp[i] + dp[i - vec[j]]) % MOD;
            }
        }
         
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            cout << dp[n] << endl;
        }
    }
}
ac;

題目4

題目鏈接
題目大意:
給出n個數字𝑎1,𝑎2,⋯,𝑎𝑛 ,要求構造一個長度不超過300的整數數組b,要求:
數組b中沒有重複的元素;
數組b包括了數組a的所有數字;
數組b任意兩個數字的差,其絕對值可以在數組b中找到相同數字。

輸入:
第一行是整數t,表示有t個樣例 (1≤𝑡≤50 ).
每個樣例第一行是整數𝑛 (2≤𝑛≤100);
第二行是n個整數 𝑎1,𝑎2,⋯,𝑎𝑛 (−100≤𝑎𝑖≤100)

輸出:
如果有解,先輸出YES,再輸出整數k,表示有k個整數; (𝑛≤𝑘≤300)
𝑏1,𝑏2,⋯,𝑏𝑘 (−1e9≤𝑏𝑖≤1e9)
如果無解則輸出NO;

Examples
input
4
3
3 0 9
2
3 4
5
-7 3 13 -2 8
4
4 8 12 6

output
yes
4
6 0 3 9
yEs
5
5 3 1 2 4
NO
Yes
6
8 12 6 2 4 10

題目解析:
構造出來的數組b中不會存在負數,證明:
假設a[i]-a[j],a[j]小於零,則必然需要一個比a[i]的數字a[k],但是a[k]-a[j]又會產生更大的數字;

所以數組a中存在負數無解;
其他的情況,就用1、2、3、4到max來填充即可。

class Solution {
    static const int N = 200010;
public:

public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            int maxNum = 0, minNum = 200;
            for (int i = 1; i <= n; ++i) {
                int k;
                cin >> k;
                maxNum = max(maxNum, k);
                minNum = min(minNum, k);
            }
            if (minNum < 0) {
                cout << "NO" << endl;
            }
            else {
                cout << "YES" << endl;
                cout << maxNum + 1 << endl;
                for (int i = 0; i <= maxNum; ++i) {
                    cout << i << " ";
                }
                cout << endl;
            }
        }
    }
}
ac;

題目5

題目鏈接
題目大意:
給出n個整數的數組,從左到右可以依次選擇若干個整數,要求累加和在過程中始終不能爲負數。
已知初始數字和爲0,想知道最多能選擇多少個數字。

輸入:
第一行是整數 𝑛 (1≤𝑛≤2000)
第二行是n個整數𝑎1 , 𝑎2, ... ,𝑎𝑛 (−1e9≤𝑎𝑖≤1e9)
輸出:
輸出能選擇的最多整數。

Examples
input
6
4 -4 1 -3 1 -3
output
5

題目解析:
一種簡單的策略:
遇到正的就喫,遇到負的就看當前能否喫下,能夠喫則直接喫;
如果不能喫,則考慮是否將喫過的負數吐出來,如果存在某個負數的絕對值 比這個數字的絕對值要大,則可以把原來的負數吐出來,把這個數字喫進去;

可以用優先隊列來記錄負數,複雜度O(NlogN);

class Solution {
    static const int N = 200010;
public:
    int a[N];
    priority_queue<int, vector<int>, greater<int> > q;
public:
    void solve() {
        int t = 1;
        while (t--) {
            int n;
            cin >> n;
            while (!q.empty()) {
                q.pop();
            }
            lld sum = 0, ans = 0;
            for (int i = 0; i < n; ++i) {
                int tmp;
                scanf("%d", &tmp);
                if (sum + tmp >= 0) {
                    ++ans;
                    sum += tmp;
                    if (tmp < 0) {
                        q.push(tmp);
                    }
                }
                else {
                    int top = 0;
                    if (!q.empty()) {
                        top = q.top();
                    }
                    if (top < tmp) {
                        q.pop();
                        q.push(tmp);
                        sum = sum - top + tmp;
                    }
                }
            }
            cout << ans << endl;
            
        }
    }
}
ac;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章