前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >程序员进阶之算法练习(六十八)

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

作者头像
落影
发布2022-10-04 17:30:19
2010
发布2022-10-04 17:30:19
举报
文章被收录于专栏:落影的专栏落影的专栏

题目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

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

代码语言:javascript
复制
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的数字可以枚举,更大的数字必然有解。

代码语言:javascript
复制
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的时候,为了字典序最小,全部加到最末尾的整数即可;

代码语言:javascript
复制
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的数字,这个不就是背包嘛。

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

代码语言:javascript
复制
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;
                }
            }
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目1
  • 题目2
  • 题目3
  • 题目4
  • 题目5
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档