[Offer收割]编程练习赛34

A 共同富裕

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个数组A1, A2, ... AN,每次操作可以从中选定一个元素Ai,把除了Ai之外的所有元素都加1。

问最少几次操作可以实现“共同富裕”,即数组中所有元素都相等。

例如对于[1, 1, 1, 2, 3]经过3步:[1, 1, 1, 2, 3] -> [2, 2, 2, 3, 3] -> [3, 3, 3, 3, 4] -> [4, 4, 4, 4, 4]。

输入
第一行包含一个整数N。(1 ≤ N ≤ 100000)
以下N行包含N个整数A1, A2, ... AN。 (1 ≤ Ai ≤ 100000)

输出
最小的操作数

样例输入
5
1
1
1
2
3
样例输出
3

思路一

每轮选择n-1个加一相当于每轮选择一个减一,所以答案就是将所有值见到最小值所需次数。复杂度O(n)。典型的正难则反

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int a[N];
int main() {
    int n;
    cin >> n;
    ll sum = 0;
    ll temp = 0;
    ll mn = N;
    for (int i = 0; i < n; i++) {
        cin >> temp;
        sum += temp;
        mn = min(temp, mn);
    }
    cout << sum - mn * n << "\n";
    return 0;
}

思路二

假设加了m轮,则最后的和为sum(a) + m * (n-1)。所以有:
{sum(a) + m * (n-1)} % n == 0
min(a) + m >= {sum(a) + m * (n-1)} / n >= max(a)
注意溢出

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int a[N];
int main() {
    int n;
    cin >> n;
    ll sum = 0;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        sum += a[i];
    }
    sort(a, a + n);
    long long remain = sum % n;
    long long avg = sum / n + remain;
    while (a[0] + remain < avg) {
        avg += n - 1;
        remain += n;
    }
    cout << remain << "\n";
    return 0;
}

B 股票价格3

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi最近在关注股票,为了计算股票可能的盈利,他获取了一只股票最近N天的价格A1~AN。

小Hi想知道,对于第i天的股票价格Ai,几天之后股价会第一次超过Ai。

假设A=[69, 73, 68, 81, 82],则对于A1=69,1天之后的股票价格就超过了A1;对于A2=73,则是2天之后股票价格才超过A2。

输入
第一行包含一个整数N。

以下N行每行包含一个整数Ai。

对于50%的数据,1 ≤ N ≤ 1000

对于100%的数据,1 ≤ N ≤ 100000, 1 ≤ Ai ≤ 1000000

输出
输出N行,其中第i行代表对于第i天的股票价格Ai,几天之后股价会第一次超过Ai。

如果Ai+1~AN之内没有超过Ai,输出-1。

样例输入
5
69
73
68
81
82
样例输出
1
2
1
1
-1

思路一

pair<value, index> 排序,用一个set表示可以用的index。遍历排序后的数组二分找出满足的最小index即可,每次遍历之后将该index从set中删除,即不可用。
复杂度 O(nlgn)
注意corner case: 数组中有一样的值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
typedef pair<int, int> p;
p a[N];
int ans[N];
int n;
int main() {
    set<int> s;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i].first;
        a[i].second = i;
        s.insert(i);
    }
    sort(a, a + n);
    for (int i = 0; i < n; i++) {
            int j = i;
            while (j < n && a[j].first == a[i].first) j++;
            if (j > i + 1) {
                for (int k = i; k < j && k < n && s.count(a[k].second); k++) {
                    int cur = a[k].second;
                    s.erase(cur);
                }
            }
            int cur = a[i].second;
            auto it = s.upper_bound(cur);
            if (it == s.end()) {
                ans[cur] = -1;
            } else {
                ans[cur] = *it - cur;
            }
            s.erase(cur);
    }
    for (int i = 0; i < n; i++) {
        cout << ans[i] << "\n";
    }
    return 0;
}

思路二 线段树或树状数组

倒序处理数组,维护a[i+1] ~ a[n-1] 的线段树,可快速找出满足最小的j使得a[j] > a[i] 。
复杂度O(nlgn)

单调栈

从前往后扫,维护一个递减的单调栈:对于当前元素a[i] 以:
*若当前元素小于等于栈顶的,则直接入栈

  • 否则, 将栈内的元素出栈直至栈顶大于a[i],对于出栈的元素对应的结果就是i
    复杂度O(n)
#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5;
int A[maxn];
int ans[maxn];

int main() {
    int n;
    cin>>n;
    for (int i = 0; i < n; ++i)
        cin>>A[i];

    stack<pair<int, int> > s;
    for (int i = 0; i < n; ++i) {
        if (s.empty() || s.top().first >= A[i])
            s.push({A[i], i});
        else {
            while (!s.empty() && s.top().first < A[i]) {
                ans[s.top().second] = i - s.top().second;
                s.pop();
            }
            s.push({A[i], i});
        }
    }
    while (!s.empty()) {
        ans[s.top().second] = -1;
        s.pop();
    }
    for (int i = 0; i < n; ++i)
        printf("%d\n", ans[i]);
    return 0;
}

C 超市规划

D 有趣的子区间

posted @ 2017-11-08 17:33  hxidkd  阅读(140)  评论(0编辑  收藏  举报