程序员进阶之算法练习(五十四)

正文

题目1

题目链接
题目大意:
给出n个整数,还有一个数字d;
要求去除最少的数字,使得剩余数字的最大最小值差不大于d;

输入数据:
n and d (1 ≤ n ≤ 100, 0 ≤ d ≤ 100)
(1 ≤ x[i] ≤ 100)

Examples
input
3 1
2 1 4
output
1

题目解析:
方法1:
贪心。假设最后的结果是区间是[left, right],那么小于left、大于right的数字全部要抛弃。
先对数组排序,假设数字a[i]是left,那么通过二分查找right=a[i]+d,可以快速算出应该要抛弃的数字。

方法2:
暴力。先排序,枚举保留的数据区间[left, right],计算是否满足最大最小值差小于d。

方法3:
扫描线。先排序,从左到右扫描,保持一个最大最小值差小于d的区间;如果区间不满足,则从区间左边抛弃元素。

int a[N];

int main(int argc, const char * argv[]) {
    // insert code here...
    int n, d;
    cin >> n >> d;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    
    sort(a, a + n);
    int ans = n;
    for (int i = 0; i < n; ++i) {
        int right = upper_bound(a, a + n, a[i] + d) - a;
        ans = min(ans, i + n - right);
    }
    cout << ans << endl;
    
    
    return 0;
}

题目2

题目链接
题目大意:
给出四个整数n,k,A,B;
现在需要把数字n变成数字1,每次有两个操作:
1、n=n-1, 代价为A;
2、n=n/k,代价为B;(要求是n能被k整除)
求最小代价。

输入数据:
四行整数,分别表示 n、k、A、B (1 ≤ n,k,A,B ≤ 2·1e9)

Examples
input
9
2
3
1
output
6

样例解释:
Subtract 1 from x (9 → 8) paying 3 coins.
Divide x by 2 (8 → 4) paying 1 coin.
Divide x by 2 (4 → 2) paying 1 coin.
Divide x by 2 (2 → 1) paying 1 coin.

题目解析:
直接的做法,每次判断操作的代价,选择最小的代价进行操作,直到数字变为1。
但是因为n的数字较大,如果出现极端的情况,可能会进行n-1次操作1,这样使得复杂度过高。

换一种思路,操作2只能发生在n%k==0的情况,那么只需判读数字n变成n/kk的操作代价是否划算。
假设t=n/k
k,那么如果数字t进行操作2都不划算,那么往后所有的操作2都是不划算的。

思考🤔:
代码很简洁。

int main(int argc, const char * argv[]) {
    lld n, k, a, b;
    cin >> n >> k >> a >> b;
    lld ans = (n - 1) * a;
    while (n > 1) {
        lld t = n / k * k;
        if ((t - t / k) * a <= b) {
            break;
        }
        else {
            n = t / k;
            ans = ans - (t - t / k) * a + b;
        }
    }
    cout << ans << endl;
    return 0;
}

题目3

题目链接
题目大意:
给出一个长度为n的字符串str,字符串由小写字母拼成;
现需要拼一个新字符串,要求:
1、长度为k,全部为小写字母,且字母都在str中出现过;
2、新字符串的字典序大于str,且尽可能小;

输入数据:
第一行 n and k (1 ≤ n, k ≤ 100 000)
第二行 字符串str

Examples
input
3 3
abc
output
aca
样例解释:
aaa, aab, aac, aba, abb, abc, aca, acb, .... 都是满足条件1;
其中字典序大于abc,且尽可能小的是aca;

题目解析:
题目分俩种情况讨论:
1、k > n,那么只需要用str中最小的字符填满(strNew-str)后面的字符;
2、k <= n,从右往左遍历,寻找某一位i,strNew[i]>str[i],之后的字符全部用str中最小的字符填满。

思考🤔:
也可以借用模拟加法的方式来思考,比如说abc的下一个字符串是abc+a=abd,d进位变成aca。

char str[N];
int vis[3333];

int main(int argc, const char * argv[]) {
    int n, k;
    cin >> n >> k;
    cin >> str;
    for (int i = 0; i < n; ++i) {
        vis[str[i]] = 1;
    }
    if (k > n) {
        cout << str;
        for (int i = 0; i < 26; ++i) {
            int index = 'a' + i;
            if (vis[index]) {
                for (int j = 0; j + n < k; ++j) {
                    putchar(index);
                }
                break;
            }
        }
        cout << endl;
    }
    else {
        for (int i = k - 1; i >= 0; --i) {
            int bigger = 0;
            for (int j = str[i] + 1; j < 'a' + 26; ++j) {
                if (vis[j]) {
                    bigger = j;
                    break;
                }
            }
            if (bigger) {
                str[i] = bigger;
                
                for (int j = 0; j < 26; ++j) {
                    int index = 'a' + j;
                    if (vis[index]) {
                        for (int t = i + 1; t < k; ++t) {
                            str[t] = index;
                        }
                        break;
                    }
                }
                break;
            }
        }
        str[k] = 0;
        cout << str << endl;
    }
    return 0;
}

题目4

题目链接
题目大意:
我们用一个字符串来描述一串项链,字符串由'o'和'-'组成,o表示珠子,-表示链条;(字符串第一个字符和最后一个字符相连)
现在可以对项链进行重新组合,即对'o' '-'的字符串重新排列,问是否能满足要求:
珠子之间链条的数量是相同;

输入:
第一行:字符串str,表示项链;(注意,可能出现一个珠子、多个珠子、没有珠子的情况)
输出:
YES如果能满足要求,NO如果不能满足要求;

输入数据:

Examples
input
-o-o--
output
YES

题目解析:
先统计-和o的数量x,y;
分类讨论:
y=0的时候,那么必然是YES;
y!=0,那么当x%y==0的时候,是YES;否则是NO;

int main(int argc, const char * argv[]) {
    string str;
    cin >> str;
    int x = 0, y = 0;
    for (int i = 0; i < str.length(); ++i) {
        if (str[i] == '-') {
            ++x;
        }
        else {
            ++y;
        }
    }
    if (y == 0 || x % y == 0) {
        cout << "YES" << endl;
    }
    else {
        cout << "NO" << endl;
    }
    return 0;
}

题目5

题目链接
题目大意:
小明正在上图像算法课程,老师要求他实现一个图像滤镜算法,其处理过程可以这么描述:
给出n个数字p[i],[0, 255]范围,表示颜色;
把范围[0, 255]划分多个区间比如说[0, 4], [5, 9], [10, 14]....要求区间的大小不超过k;(注意区间要求是连续的,区间数量没有要求,区间的长度有限制)
然后对数据进行处理,区间[0, 4]内的数字都可以用0表示,同理[5, 9]=5;
要求处理之后所有的数字形成的字典序最小。

输入:
第一行 n,k ( 1 ≤ n ≤ 1e5 , 1 ≤ k ≤ 256 )
第二行 p 1 , p 2 , … , p n ( 0 ≤ p i ≤ 255 )

输出:
处理字后的n个数字。

Examples
input
4 3
2 14 3 4
output
0 12 3 3

样例解释
color 2 属于 group [0, 2] = 0
color 14 = group [12, 14] = 12
color 3,4 = group [3, 5] = 3
所以最终数字是0,12,3,3

题目解析:
所有的数字形成的字典序最小,相当于前面的数字越小越好。
那么在考虑第i个数字的时候,可以不管i+1之后的数据,尽可能满足第i个数字最小。
由此,我们可以得到一个贪心策略:
默认[0, 255]都不分配区间,对第i个数字,其颜色值p[i],我们从p[i]-1开始往前找还没分配的区间,这时会有两种情况:
1、都没有分配,那么我们可以把(p[i] - k + 1, p[i])分配成一个区间;
2、找到一个已经分配的区间(x, y),那么看这个区间的长度是否能达到(x, p[i]),如果可以则把区间放大成(x, p[i]);
如果长度不够,那么则从(y + 1, p[i])分配一个区间;
这样可以得到一个最小字典序。

int main(int argc, const char * argv[]) {
    int n, k;
    cin >> n >> k;
    int cnt = 1;
    for (int index = 0; index < n; ++index) {
        int x;
        cin >> x;
        if (!color[x]) {
            int cur, ok = 0;
            for (cur = x - 1; cur >= 0 && cur + k > x; --cur) {
                if (color[cur]) {
                    int len = checkLen(cur);
                    if (len + (x - cur) <= k) {
                        ok = color[cur];
                    }
                    break;
                }
            }
            
            if (ok) {
                while (cur <= x) {
                    color[cur++] = ok;
                };
            }
            else {
                while (cur < x) {
                    ++cur;
                    color[cur] = cnt;
                }
                cnt++;
            }
        }
        cout << x - checkLen(x) + 1 << " ";
    }
    return 0;
}

总结

题目1,题目要求简单,但是有很多种做法;
题目2,贪心的思路,决策的关键节点在能整除的时候;
题目3,模拟加法,用分类讨论的思路也很清晰;
题目4,分类讨论,环是模拟起来比较费脑的东西,可以画图思考;
题目5,贪心,尽可能让前面的数字更小。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容