美文网首页
程序员进阶之算法练习(六十八)

程序员进阶之算法练习(六十八)

作者: 落影loyinglin | 来源:发表于2022-10-03 10:48 被阅读0次

    题目1

    题目链接
    题目大意:
    有n个球,序号分别是1、2、3、、、、n,每个球上面有一个数字a[1]、a[2]、a[3]、、、a[n],这组数字是不递减的,即是 a[i]≤a[i+1], 1≤i<𝑛;
    现在需要给n个球染色,需要满足:
    1、每个球只有一个颜色;
    2、将某个颜色的球挑选出来,按照序号从小到大放好,上面的数字是严格递增;

    问,最少需要几个颜色。

    输入:
    第一行是𝑡,表示样例数 (1≤𝑡≤100)
    每个样例两行,第一行是整数𝑛 (1≤𝑛≤100)
    第二行是n个整数 𝑎1,𝑎2,…,𝑎𝑛 (1≤𝑎𝑖≤𝑛)

    输出:
    每个样例一行,输出最少需要的颜色数量。

    Examples
    input
    5
    6
    1 1 1 2 3 4
    5
    1 1 2 2 3
    4
    2 2 2 2
    3
    1 2 3
    1
    1

    output
    3
    2
    4
    1
    1

    题目解析:
    由于本身数字就是不递减,要满足严格递增,只需要关注数字相同的部分;
    相同数字的最大连续长度,就是需要的颜色数量。

    
    int a[N];
    
    int main(int argc, const char * argv[]) {
        // insert code here...
        
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            int ans = 1, cur = 1;
            for (int i = 0; i < n; ++i) {
                cin >> a[i];
                if (i > 0) {
                    if (a[i] == a[i - 1]) {
                        ++cur;
                        ans = max(ans, cur);
                    }
                    else {
                        cur = 1;
                    }
                }
            }
            cout << ans << endl;
        }   
        
        return 0;
    }
    

    题目2

    题目链接
    题目大意:
    小明喜欢0到9中的一个数字d,如果某个整数的十进制表示中,数字d只出现一次则称这个整数是lucky数;
    比如说d=7的时候,17就是lucky数,77就不是lucky数;
    现在给出q个整数,问给出的整数是否都能拆分为若干个lucky数之和;

    输入:
    第一行是样例数𝑡 (1≤𝑡≤9)
    每个样例两行,第一行𝑞 and 𝑑 (1≤𝑞≤1e4, 1≤𝑑≤9).
    第二行是𝑞个整数 𝑎1,𝑎2,…,𝑎𝑞 (1≤𝑎𝑖≤1e9).
    输出:
    每个样例有q行,每一行对应a[i]是否可以拆分为若干个lucky数之和;

    Examples
    input
    2
    3 7
    24 25 27
    10 7
    51 52 53 54 55 56 57 58 59 60

    output
    YES
    NO
    YES
    YES
    YES
    NO
    YES
    YES
    YES
    YES
    YES
    YES
    NO

    题目解析:
    以7为例,如果数字大于77,那么必然有解:可以拆分为7x+若干个7的组合;
    比如说77=70+7,100=7+7+7+7+72;

    那么小于100的数字可以枚举,更大的数字必然有解。

    int a[100][10]; // a[i][j]表示数字为i,幸运数字是j,是否有解
    
    int main(int argc, const char * argv[]) {
        // insert code here...
    
        for (int i = 1; i < 100; ++i) {
            for (int lucky = 1; lucky <= 9; ++lucky) {
                bool ok = 0;
                int tmp = i;
                while (tmp) {
                    if (tmp % 10 == lucky) {
                        ok = 1;
                        break;
                    }
                    tmp /= 10;
                }
                a[i][lucky] = ok;
                for (int k = 1; k <= i; ++k) {
                    if (a[k][lucky] && k + lucky < 100) {
                        a[k+lucky][lucky] = 1;
                    }
                }
            }
        }
        
        int t;
        cin >> t;
        while (t--) {
            int q, d;
            cin >> q >> d;
            while (q--) {
                int k;
                cin >> k;
                if (k >= 100) {
                    cout << "YES" << endl;
                }
                else {
                    cout << (a[k][d] ? "YES" : "NO") << endl;
                }
            }
        }   
        
        return 0;
    }
    
    

    题目3

    题目链接
    题目大意:
    n个数字的数组a,可以执行最多k次操作,每次操作是找两个数字,其中一个+1,另外一个-1;
    想知道经过最多k次操作之后,数组最小的字典序是什么;

    输入:
    第一行是样例数𝑡 (1≤𝑡≤20)
    每个样例两行,第一行是整数𝑛 and 𝑘 (2≤𝑛≤100, 1≤𝑘≤10000)
    第二行是n个整数 𝑎1 , 𝑎2, …, 𝑎𝑛 (0≤𝑎𝑖≤100)
    输出:
    每个样例一行,要求所有数字非负,且 字典序最小;

    Examples
    input
    2
    3 1
    3 1 4
    2 10
    1 0

    output
    2 1 5
    0 1

    题目解析:
    容易知道,要让字典序最小,那么就是让前面的数字尽可能小;
    所以从左到右开始选择数字,让前面的数字优先-1,收益最大;
    同理,当我们需要+1的时候,为了字典序最小,全部加到最末尾的整数即可;

    
    class Solution {
        static const int N = 100010;
    public:
        int n, m;
        int a[N], b[N], c[N], ans[N];
    public:
        void solve() {
            int t;
            cin >> t;
            while (t--) {
                cin >> n >> m;
                for (int i = 0; i < n; ++i) {
                    cin >> a[i];
                }
                for (int i = 0; i < n - 1; ++i) {
                    if (m >= a[i]) {
                        a[n - 1] += a[i];
                        m -= a[i];
                        a[i] = 0;
                    }
                    else {
                        a[n - 1] += m;
                        a[i] -= m;
                        m = 0;
                    }
                }
                for (int i = 0; i < n; ++i) {
                    cout << a[i] << " ";
                }
                cout << endl;
            }
        }
    }
    ac;
    

    题目4

    题目链接
    题目大意:
    有n个整数的数组a,现在将数组分成两个集合,如果两个集合内的整数之和相等,则是不美好的;
    现在希望去掉若干个数字,要求n个数字无法拆成两个集合,这两个集合的和是相等的。

    输入:
    第一行是整数𝑛 (2≤𝑛≤100)
    第二行是n个整数𝑎1 , 𝑎2, …, 𝑎𝑛 (1≤𝑎𝑖≤2000)
    输出:
    第一行是需要移除的整数个数m,第二行是m个需要移除整数的下标;

    Examples
    input
    4
    6 3 9 12
    output
    1
    2

    题目解析:
    假设n个数字分成集合a[x]和b[y],并且sum(a) = sum(b),那么sum(a)=sum(n)/2。
    最开始的想法是,如果sum(a)=sum(b),那么只要去掉一个集合a或者b中的奇数,那么必然会出现sum(a)!=sum(b); (因为奇数和偶数必定不相同)
    问题就变成题目中是否存在一个解,使得sum(a)==sum(b) :
    如果有存在,则去掉n个数字中的奇数;
    如果不存在,则不需要去掉任何数字;

    注意,这里很容易产生误解:觉得去掉最小/最大的整数也是可行的。
    例子: 4 4 6 6 8 8
    去掉4之后可以拆分为 4 + 6 + 6 = 8 + 8
    去掉8之后可以拆分为 4 + 4 + 6 = 6 + 8
    直接找一个数字最小、最大都无法直接确定。

    我们回到最初判断,我们会首先认为,如果sum(n)是奇数,则无解;
    那么假如数组中存在一个奇数,我们只要去掉这个奇数即可。
    那如果数组中一个奇数都没有呢?
    假如数组都是偶数,假设最终分出来的两个集合a和b,我们对两边的集合除以2,不影响sum(a)=sum(b);
    如果还是没有奇数,我们可以继续这样操作。容易知道,这样是一定可以找到一个奇数。
    根据上面的思路,我们把每一个数字看成二进制,最右边1出现之后,就是奇数了。那么即是寻找n个数字中,最右边1最早出现的位置。

    如果要判断n个数字中,能不能凑出来sum(n)/2的数字,这个不就是背包嘛。

    class Solution {
        static const int N = 101, M = 101*2000;
    public:
        int n, m;
        int a[N], dp[M], ans[N];
        
        int bitpos(int n) {
            int pos = 0;
            while (n % 2 == 0) {
                n /= 2;
                pos++;
            }
            return pos;
        }
        
    public:
        void solve() {
            cin >> n;
            int sum = 0;
            for (int i = 0; i < n; ++i) {
                cin >> a[i];
                sum += a[i];
            }
            
            bool ok = 1;
            if (sum % 2) {
                ok = 0;
            }
            else {
                sum /= 2;
                dp[0] = 1;
                
                for (int i = 0; i < n; ++i) {
                    for (int j = sum; j > 0; --j) {
                        if (j >= a[i]) {
                            dp[j] |= dp[j-a[i]];
                        }
                    }
                }
                ok = dp[sum];
            }
                    
            if (ok) {
                int minIndex = 0, minPos = bitpos(a[0]);
                for (int i = 1; i < n; ++i) {
                    if (bitpos(a[i]) < minPos) {
                        minPos = bitpos(a[i]);
                        minIndex = i;
                    }
                }
                cout << 1 << " " << minIndex + 1 << endl;
            }
            else {
                cout << 0 << endl;
            }
        }
        
    }
    

    题目5

    题目链接
    题目大意:
    有n * k个整数分别为 1、2、3、4、、、n * k,现在需要将这些整数分成n行,并且对于每一行都满足:
    任意选择区间[l, r],区间的平均数是整数;
    问,是否存在这样的分配方式;

    输入:
    第一行,整数𝑡 表示t个样例 (1≤𝑡≤500)
    每个样例一行,整数𝑛 and 𝑘 (1≤𝑛,𝑘≤500)

    输出:
    每个样例,如果无解则输出NO;
    如果有解则输出YES,接下来n行分别输出k个数字;

    Examples
    input
    4
    1 1
    2 2
    3 3
    3 1

    output
    YES
    1
    YES
    1 3
    2 4
    NO
    YES
    1
    2
    3

    题目解析:
    按照题目的要求,对于n行数字,每行数字的任意区间的平均数都是整数;
    当区间长度为2时,(a[i][j] + a[i][j+1])/ 2能够整除,那么必须是两个奇数或者两个偶数;
    由此我们知道,当k>1的时候,肯定每一行数字都是奇数,或者都是偶数;(n=1或者k=1结果较为简单,这里不做讨论)

    那么可以推断出, 如果nk是奇数,那么最终肯定会出现奇数个数字,无法满足要求;
    当n
    k是偶数时,如果n是奇数,则k是偶数,那么在平均分配奇偶数的时候,必然会在第(n+1)/2行出现奇偶数混杂的情况,无法满足要求;
    如果n是偶数,那么就可以按照1、3、5、7、、这样分配所有奇数,2、4、6、8这样分配所有偶数;
    任意区间的平均数,都是中间两个数的平均值;

    class Solution {
        static const int N = 201010;
        char str[N];
        int dp[N];
    
    public:
        void solve() {
            int t;
            cin >> t;
            while (t--) {
                int n, k;
                cin >> n >> k;
                if (k == 1) {
                    cout << "YES" << endl;
                    for (int i = 0; i < n; ++i) {
                        cout << i + 1 << endl;
                    }
                }
                else if (n % 2) {
                    cout << "NO" << endl;
                }
                else {
                    cout << "YES" << endl;
                    for (int i = 0; i < n / 2; ++i) {
                        int tmp = i * k * 2 + 1;
                        for (int j = 0; j < k; ++j) {
                            cout << tmp + j * 2 << " ";
                        }
                        cout << endl;
                        for (int j = 0; j < k; ++j) {
                            cout << tmp + 1 + j * 2 << " ";
                        }
                        cout << endl;
                    }
                }
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:程序员进阶之算法练习(六十八)

          本文链接:https://www.haomeiwen.com/subject/cpzqartx.html