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

正文

题目1

题目链接
题目大意:
给出一个整数n,求一个最大整数满足:
1、整数各个数字加起来等于n;
2、没有两个相同的数字相邻;
3、数字中不包括0;

比如说n=2,满足条件1的整数有11、2、20等,但是满足条件1、2、3的就是数字2;

输入:
第一行,整数𝑡 表示t个样例 (1≤𝑡≤1000)
每个样例一行,整数𝑛 (1≤𝑛≤1000)
输出:
每个样例输出一行,满足条件的最大整数。

Examples
input
5
1
2
3
4
5
output
1
2
21
121
212

题目解析:
题目要求的数字尽可能的大,那么数字的位数会比数字的大小更加重要,那么可以论证最终数字中不存在大于3的数字;
假如最大整数是3xx的整数,那么可以构造出来21xx使得结果更大;
同时由于条件2的存在,结果必然是以1212或者2121这样的交替结果。
为了便于计算,我们将n%3,得到一个余数q;
q=0时,结果是2121...;
q=1时,结果是12121...;
q=2时,结果是21212...;

class Solution {
   static const int N = 200010;
   string str;
   int a[N];

public:
   void solve() {
       int t;
       cin >> t;
       while (t--) {
           int n;
           cin >> n;
           int k = n % 3;
           if (k == 0) {
               for (int i = 0; i < n / 3; ++i) {
                   cout << "21";
               }
               cout << endl;
           }
           else if (k == 1) {
               cout << 1;
               for (int i = 0; i < n / 3; ++i) {
                   cout << "21";
               }
               cout << endl;
           }
           else {
               cout << 2;
               for (int i = 0; i < n / 3; ++i) {
                   cout << "12";
               }
               cout << endl;
           }
       }
   }
}
ac;

题目2

题目链接
题目大意:
餐馆有三种食物,数量分别是a、b、c个;
餐馆给客人提供就餐的特点:
1、每个人至少吃到一种食物;
2、每个人,每种食物最多吃1个;
3、每个人要吃不同组合的食物;

问,最多能给几个人提供就餐服务?

输入:
第一行,整数𝑡,表示样例个数 (1≤𝑡≤500)
接下来t个样例,每个1行,整数𝑎 , 𝑏 and 𝑐 (0≤𝑎,𝑏,𝑐≤10)
输出:
每个样例1行,能提供就餐服务的最多人数;

Examples
input
7
1 2 1
0 0 0
9 1 7
2 2 3
2 3 2
3 2 2
4 4 4
output
3
0
4
5
5
5
7

题目解析:
用1来表示分配,0表示不分配。
每种食物,单独分给1个人,肯定是最优的,可以优先分配;(100,010,001)
食物两两组合的时候,优先考虑剩下量最大的一种;(011,101,110)
如果还有剩下食物和人,则考虑分配3种食物的情况;(111)

int main(int argc, const char * argv[]) {
    // insert code here...
    
    int t;
    cin >> t;
    while (t--) {
        int a[3];
        cin >> a[0] >> a[1] >> a[2];
        int cnt = 0;
        for (int i = 0; i < 3; ++i) {
            if (a[i]) {
                --a[i];
                ++cnt;
            }
        }
        sort(a, a+3, cmp);
        for (int i = 0; i < 3; ++i) {
            for (int j = i + 1; j < 3; ++j) {
                if (a[i] && a[j]) {
                    --a[i];
                    --a[j];
                    ++cnt;
                }
            }
        }
        if (a[0]&&a[1]&&a[2]) {
            ++cnt;
        }
        
        cout << cnt << endl;
        
    }
    
    
    return 0;
}

题目3

题目链接
题目大意:
n个人参加比赛,比赛分上半场和下半场,分别会产生1~n的名次;(没人并列)
最终的比赛排名,是根据两场比赛的名次之和进行排列;
比如说小明参加5个人的比赛,上半场和下半场分别拿了第1名和第3名,名次之和是4,那么有两种可能性,
最好的情况:


最坏的情况:

现已知小明的上下半场的名次分别是x和y,最好和最坏的名次,分别是多少?

输入:
第一行,整数𝑡,表示样例个数 (1≤𝑡≤100)
接下来t行,每行有整数𝑛, 𝑥, 𝑦 (1≤𝑛≤10^9, 1≤𝑥,𝑦≤𝑛)

输出:
每个样例一行,两个整数,分别表示最好和最坏的名次

Examples
input
1
5 1 3
output
1 3

题目解析:
分两种情况来考虑,先看�名次最好的情况;(a、b顺序没有关系,我们另a<b)
假设n个人,小明的名次是a+b,那么应该尽量安排其他人的名次>=a+b+1,我们另k=a+b+1;
k<=n的时候,其他人可以一大一小(n配1、n-1配2这样),那小明最好的名次是1;
如果k>n的时候,有k-n-1个人,肯定无法分配使得名次比小明差,我们把这些人提取出来,小配小,比如说1配1,2配2;(贪心的思想)
又因为小明本身占用了名次a和b,如果b还在剩下的名次里面,还要占用一个名额,小明最好的名次是k-n-1-1;如果b<k-n,则不需要关注,此时名次是k-n-1;

如果是名次最差的情况,那么只需要尽可能分配其他的名次,使得名次之和大于等于小明;
小明的名次之和是k=a+b,如果k>n,则必然是最后一名;(其他人可以1配n,2配n-1这样)
如果k<n,最多只能找到(n-k-2)个人,名次组合和小明一样;(假如8个人,小明是2+3,那么必然是1+4,3+2,4+1)
那么小明就是k-1名;

int main(int argc, const char * argv[]) {
    // insert code here...
    
    int t;
    cin >> t;
    while (t--) {
        int n, a, b;
        cin >> n >> a >> b;
        if (a > b) {
            swap(a, b);
        }
        int ansFirst, ansLast;
        int k = a + b + 1;
        if (k <= n) {
            ansFirst = 1;
        }
        else {
            ansFirst = k - n - 1;
            if (b >= k - n) {
                ++ansFirst;
            }
        }
        
        int x = a + b;
        if (x > n) {
            ansLast = n;
        }
        else {
            ansLast = x - 1;
        }
        
        cout << ansFirst << " " << ansLast << endl;
    }
    
    
    return 0;
}

题目4

题目链接
题目大意:
建筑公司要建n栋楼(排成一行),第i栋的设计最大高度是m[i];
并且当地城市有规定:每栋楼不能在左右两边同时出现比它高的建筑;
用数学的语言来描述,即是每栋楼的最终高度a[i]必须要小于等于m[i];不能存在j和k,满足j<i<k并且a[j]>a[i]<a[k];
现在想知道建筑公司如何分配每栋楼的高度,使得最终所有楼的总高度最大?

输入:
第一行整数𝑛 (1≤𝑛≤1000)
第二行整数𝑚1,𝑚2,…,𝑚𝑛 (1≤𝑚𝑖≤10^9)

输出:
n个整数a[i],如果有多个分配组合,输出任何一个总高度最大的组合;

Examples
input
5
1 2 3 2 1
output
1 2 3 2 1

题目解析:
根据题目的要求,可以知道最终的高度,是一个递增再递减的序列;
即是存在k,从k到1,数字逐渐递减;从k到n,数字逐渐递增;
由于题目n范围较小,枚举这个k,既可以知道最优解;

思考🤔:
注意题目范围, 数字之和会超过int32;


int a[N], ans[N], f[N];

int main(int argc, const char * argv[]) {
    // insert code here...
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    
    lld sumMax = 0;
    for (int k = 0; k < n; ++k) {
        f[k] = a[k];
        int tmp = k - 1;
        while (tmp >= 0) {
            f[tmp] = min(a[tmp], f[tmp + 1]);
            --tmp;
        }
        tmp = k + 1;
        while (tmp < n) {
            f[tmp] = min(a[tmp], f[tmp  - 1]);
            ++tmp;
        }
        
        lld sumTmp = 0;
        for (int i = 0; i < n; ++i) {
            sumTmp += f[i];
        }
        if (sumTmp > sumMax) {
            sumMax = sumTmp;
            memcpy(ans, f, sizeof(int) * n);
        }
    }
    for (int i = 0; i < n; ++i) {
        cout << ans[i] << " ";
    }
    cout << endl;
    
    return 0;
}

题目5

题目链接
题目大意:
建筑公司要建n栋楼(排成一行),第i栋的设计最大高度是m[i];
并且当地城市有规定:每栋楼不能在左右两边同时出现比它高的建筑;
用数学的语言来描述,即是每栋楼的最终高度a[i]必须要小于等于m[i];不能存在j和k,满足j<i<k并且a[j]>a[i]<a[k];
现在想知道建筑公司如何分配每栋楼的高度,使得最终所有楼的总高度最大?

输入:
第一行整数𝑛 (1≤𝑛≤500000)
第二行整数𝑚1,𝑚2,…,𝑚𝑛 (1≤𝑚𝑖≤10^9)

输出:
n个整数a[i],如果有多个分配组合,输出任何一个总高度最大的组合;

Examples
input
5
1 2 3 2 1
output
1 2 3 2 1

题目解析:
由前面可以知道,最终结果必然是由一段非严格递增的数字,加上一段非严格递减的数字;
我们用dpLeft[i]来表示前i个数字,保持非严格递增的最大高度总和,那么有dpLeft[i]可以由dpLeft[1]到dpLeft[i-1]算出来;这个状态转移的复杂度是O(N);
以样例的数据来看,当我们在计算1、2、3这个三个数字的dpLeft[3]的时候,dpLeft[3]可以直接由dpLeft[2]来递推;
从i-1开始,找到第一个比a[i]小的数字k,我们有dpLeft[i]=dpLeft[k]+(i - k) * a[i];

同理,我们用dpRight[i]来表示从i到n的数字,保持非严格递减的数字;同样可以得到dpRight[i] = dpRight[k]+(k-i)*a[i];
然后遍历1~n,ans=max(ans, dpLeft[i] + dpRight[n+1]);
得到最大的结果。

思考🤔:
单调性是进阶必备。

lld a[N], dpLeft[N], dpRight[N], ans[N];
stack<int> vec; // pos

int main(int argc, const char * argv[]) {
    // insert code here...
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &a[i]);
    }
    
    vec.push(0);
    for (int i = 1; i <= n; ++i) {
        while (!vec.empty()) {
            int pos = vec.top();
            if (a[i] <= a[pos]) {
                vec.pop();
            }
            else {
                break;
            }
        }
        dpLeft[i] = dpLeft[vec.top()] + (i - vec.top()) * a[i];
        vec.push(i);
//        cout << i << " left " << dpLeft[i] << endl;
    }
    
    while (!vec.empty()) {
        vec.pop();
    }
    vec.push(n+1);
    for (int i = n; i >= 1; --i) {
        while (!vec.empty()) {
            int pos = vec.top();
            if (a[i] <= a[pos]) {
                vec.pop();
            }
            else {
                break;
            }
        }
        dpRight[i] = dpRight[vec.top()] + (vec.top() - i) * a[i];
        vec.push(i);
//        cout << i << " right " << dpRight[i] << endl;
    }
    
    lld sum = 0, pos = 0;
    for (int i = 1; i <= n; ++i) {
        if (dpLeft[i] + dpRight[i + 1] > sum) {
            sum = dpLeft[i] + dpRight[i + 1];
            pos = i;
        }
    }
    
    
    ans[pos] = a[pos];
    lld tmp = pos - 1;
    while (tmp >= 1) {
        ans[tmp] = min(a[tmp], ans[tmp + 1]);
        --tmp;
    }
    ans[pos + 1] = a[pos + 1];
    tmp = pos + 2;
    while (tmp <= n) {
        ans[tmp] = min(a[tmp], ans[tmp  - 1]);
        ++tmp;
    }
    
    for (int i = 1; i <= n; ++i) {
        printf("%lld ", ans[i]);
    }
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容