Форум программистов, компьютерный форум, киберфорум
Алгоритмы
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.50/18: Рейтинг темы: голосов - 18, средняя оценка - 4.50
0 / 0 / 0
Регистрация: 11.01.2025
Сообщений: 4

За один ход увеличить или уменьшить любой элемент массива на 1, всего таких операций можно сделать не больше K

11.01.2025, 17:09. Показов 5804. Ответов 77
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Дан список A из N чисел, а также число K. Можно за один ход увеличить или уменьшить любой элемент массива на 1, всего таких операций можно сделать не больше K раз.
Какая макс. длина может быть у какого-то подотрезка, в которым все элементы одинаковые?

В первой строке два числа - N и K. Во второй строке N чисел, i-тый элемент равен A[i].

Ограничения:
1 ≤ N ≤ 100 000
0 ≤ K ≤ 1 000 000 000
0 ≤ A[i] ≤ 100 000 000

Ввод:
11 7
3 5 8 9 2 7 6 5 3 9 7

Вывод:
4

Объяснение:
Можно выбрать отрезок 2-7-6-5. Выполним операцию 4 раза на первом элементе, тогда получаем 6-7-6-5. Выполним операцию 1 раз на втором элементе, итого 5 операции, получаем 6-6-6-5. Выполним операцию на последнем элементе, итого 6 операции, получаем 6-6-6-6. Все элементы одинаковы, значит размер отрезка (4) подходит. Можно показать, что больше такого отрезка, с длиной больше 4, нет.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
11.01.2025, 17:09
Ответы с готовыми решениями:

За один ход любое одно из чисел уменьшить на любой степень двойки
Своя игра Петя и Маша, ходят по очереди, играют в такую математическую игру: Задано несколько натуральных чисел. За один ход...

Описать ход выполнения таких операций
С Microsoft Access вообще не знаком, но попался такое задание. Описать ход выполнения следующих операций: 1. установки цвета написания...

Как можно увеличить или уменьшить изображения в Image с помощью TrackBar-а?
Добрый ноч форум! Подскажите пожалуйсто как можно увеличить или уменшить изображения в Image с помошию TrackBar -а? Добавлено...

77
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
17.01.2025, 18:55
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Igor3D Посмотреть сообщение
Почему не использовать только одну (или 2) медианы "по центру"?
Чтобы на следующем шаге вычислить новую медиану за О(1).

Цитата Сообщение от Igor3D Посмотреть сообщение
это "красно-черное дерево", скорость вставки/удаления приличная
Логарифм. А так за О(1) окно сдвигается.
0
1963 / 819 / 114
Регистрация: 01.10.2012
Сообщений: 4,769
Записей в блоге: 2
17.01.2025, 21:06
Цитата Сообщение от Shamil1 Посмотреть сообщение
Логарифм. А так за О(1) окно сдвигается.
Зато надо побегать с медианой. А главное - использование стандартного контейнера явно проще. Скорее всего все Вы там учли с медианой верно, но.. мысли-то не мои, их еще надо осознать, привыкнуть.

А где ТС? Он так рвался оптимизировать, ну вот оно, оптимальное, нюхайте
0
30 / 24 / 7
Регистрация: 22.02.2019
Сообщений: 104
18.01.2025, 15:18
Цитата Сообщение от Igor3D Посмотреть сообщение
ну вот оно, оптимальное, нюхайте
А которое именно?

При попытке запустить решение от Shamil1 на приаттаченных данных оно упало с переполнением стека.
Удалось запустить в отдельном потоке со стеком 16MB:
Code
1
2
3
4
5
Result = 80133
 
real    0m3,720s
user    0m3,267s
sys 0m0,032s
На тех же данных решение из поста #36:
Code
1
2
3
4
5
Length = 80134, starts at 11589, ends at 91722
 
real    0m0,069s
user    0m0,020s
sys 0m0,013s
Как-то так.
Вложения
Тип файла: zip data.zip (257.3 Кб, 9 просмотров)
1
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
18.01.2025, 17:42
Цитата Сообщение от avk959 Посмотреть сообщение
упало с переполнением стека
Надо заменить хвостовую рекурсию на цикл. Компилятор C# это не умеет.

Добавлено через 25 минут
Цитата Сообщение от avk959 Посмотреть сообщение
Result = 80133
Ну да. Мой алгоритм O(N*W). На указанных данных получается во много раз медленнее.
0
30 / 24 / 7
Регистрация: 22.02.2019
Сообщений: 104
18.01.2025, 17:59
А вы не обратили внимание, что результаты отличаются?
0
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
18.01.2025, 21:12
Обратил.
0
1963 / 819 / 114
Регистрация: 01.10.2012
Сообщений: 4,769
Записей в блоге: 2
19.01.2025, 03:50
Вот мой вариант на плюсах
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <set>
#include <chrono>
 
using Range = std::pair<int, int>;
using TKey = std::pair<int, int>;
 
int k, indexM;
Range maxR, R(0, 3);
std::vector<int> data;
std::set<TKey> set;
std::set<TKey>::iterator median;
 
void Error( const char * msg )
{
    printf(msg);
    exit(0);
}
          
void ReadData( const char * filename )
{
    int count;
    std::string line;
    std::ifstream infile(filename);
 
    bool first = true;
    while (std::getline(infile, line)) {
        if (infile.fail()) {
            printf("error reading %s\n", filename);
            exit(0);
        }
        std::istringstream iss(line);
        if (first) {
            first = false;
            iss >> count >> k;
            continue;
        }
        int n;
        while (iss >> n)
            data.push_back(n);
    }
    printf("count = %d, k = %d\n", count, k);
}
 
void MoveLeft( void )
{
    auto key = TKey(data[R.first], R.first);
    bool even = (set.size() & 1) == 0;
    if (even) {
        if (key <= *median)
            ++median;
    }
    else {
        if (key >= *median)
            --median;
    }
    set.erase(key);
    k += abs(key.first - (*median).first);
    ++R.first;
}
 
void MoveRight( void )
{
    auto key = TKey(data[R.second], R.second);
    set.insert(key);
    bool even = (set.size() & 1) == 0;
    if (even) {
        if (key < *median)
            --median;
    }
    else {
        if (key > *median)
            ++median;
    }
    k -= abs(key.first - (*median).first);
    ++R.second;
}
 
int main( int argc, char * argv[] )
{
    if (argc < 2)
        Error("usage: median <file>\n");
    
    ReadData(argv[1]);
    if (data.size() < 4)
        Error("not enough data");
 
    auto begT = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 3; ++i)
        set.insert(TKey(data[i], i));
 
    auto one = set.begin();
    median = ++one;
    auto three = ++median;
    k -= (*median).first - (*one).first;
    k -= (*three).first - (*median).first;
    
    while (R.second < data.size()) {
        if ((k >= 0) || (R.second - R.first <= 3))
            MoveRight();
        else
            MoveLeft();
 
        if ((k >= 0) && (R.second - R.first > maxR.second - maxR.first)) {
            maxR = R;
            indexM = (*median).second;
        }
    }
    auto endT = std::chrono::high_resolution_clock::now();
    auto duration = duration_cast<std::chrono::milliseconds>(endT - begT);
    printf("time = %d ms, len = %d (from %d to %d), median = %d\n",
           duration.count(), maxR.second - maxR.first, maxR.first, maxR.second, indexM);
}
Печатает
count = 100000, k = 1000000000
time = 59 ms, len = 80144 (from 11615 to 91759), median = 90870
Результаты не совпадают Интересно что избавиться от трудного пересчета медианы мне не удалось. Да, здесь он короче и быстрее (за это заплачено контейнером), но по-прежнему трудноват для понимания и реализации, нет желанной простоты. Не исключено что и насвистел
0
30 / 24 / 7
Регистрация: 22.02.2019
Сообщений: 104
19.01.2025, 09:38
Цитата Сообщение от Igor3D Посмотреть сообщение
Не исключено что и насвистел
Скорее да чем нет.
Имея заданный массив, начало и конец отрезка, количество ходов можно подсчитать просто отсортировав массив на этом отрезке.
У меня на отрезке [11615, 91759] получилось 1000392967.
0
1963 / 819 / 114
Регистрация: 01.10.2012
Сообщений: 4,769
Записей в блоге: 2
19.01.2025, 17:52
Подправил, добавил тест результата
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <set>
#include <chrono>
 
using Range = std::pair<int, int>;
using TKey = std::pair<int, int>;
 
int k, bestM, bestK;
Range maxR, R(0, 3);
std::vector<int> data;
std::set<TKey> set;
std::set<TKey>::iterator median;
 
void Error( const char * msg )
{
    printf(msg);
    exit(0);
}
          
void ReadData( const char * filename )
{
    int count;
    std::string line;
    std::ifstream infile(filename);
 
    bool first = true;
    while (std::getline(infile, line)) {
        if (infile.fail()) {
            printf("error reading %s\n", filename);
            exit(0);
        }
        std::istringstream iss(line);
        if (first) {
            first = false;
            iss >> count >> k;
            continue;
        }
        int n;
        while (iss >> n)
            data.push_back(n);
    }
    printf("count = %d, k = %d\n", count, k);
}
 
void MoveLeft( void )
{
    auto key = TKey(data[R.first], R.first);
    auto oldM = *median;
    k += abs(key.first - (*median).first);
    bool even = (set.size() & 1) == 0;
    if (even) {
        if (key <= *median) {
            ++median;
            k += abs(oldM.first - (*median).first);
        }
    }
    else {
        if (key >= *median)
            --median;
    }
    set.erase(key);
    ++R.first;
}
 
void MoveRight( void )
{
    auto key = TKey(data[R.second], R.second);
    auto oldM = *median;
    set.insert(key);
    bool even = (set.size() & 1) == 0;
    if (even) {
        if (key < *median) {
            --median;
            k -= abs(oldM.first - (*median).first);
        }
    }
    else {
        if (key > *median)
            ++median;
    }
    k -= abs(key.first - (*median).first);
    ++R.second;
}
 
int main( int argc, char * argv[] )
{
    if (argc < 2)
        Error("usage: median <file>\n");
    
    ReadData(argv[1]);
    if (data.size() < 4)
        Error("not enough data");
    auto startK = k;
 
    auto begT = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 3; ++i)
        set.insert(TKey(data[i], i));
 
    median = ++set.begin();
    for (auto it : set)
        k -= abs((*median).first - it.first);
    
    while (R.second < data.size()) {
        if ((k >= 0) || (R.second - R.first <= 3))
            MoveRight();
        else
            MoveLeft();
        
        if ((k >= 0) && (R.second - R.first > maxR.second - maxR.first)) {
            maxR = R;
            bestM = (*median).second;
            bestK = k;
        }
    }
    auto endT = std::chrono::high_resolution_clock::now();
    auto duration = duration_cast<std::chrono::milliseconds>(endT - begT);
    printf("time = %d ms, len = %d (from %d to %d), median[%d] = %d, k = %d\n",
           (int) duration.count(), maxR.second - maxR.first, maxR.first, maxR.second,
           bestM, data[bestM], bestK);
 
// test
    std::vector<TKey> chk;
    for (int i = maxR.first; i < maxR.second; ++i)
        chk.push_back(TKey(data[i], i));
    std::sort(chk.begin(), chk.end());
    auto mid = chk[(chk.size() - 1) / 2];
    
    int sum = 0;
    for (auto it : chk)
        sum += abs(it.first - mid.first);
    
     printf("Test: mid[%d] = %d, testK = %d\n", mid.second, mid.first, startK - sum);
}
Теперь совпадает (у меня правая граница не входит, поэтому 91723)
count = 100000, k = 1000000000
time = 55 ms, len = 80134 (from 11589 to 91723), median[45672] = 24911, k = 996
Test: mid[45672] = 24911, testK = 996
Правду сказать, как пересчитывать "k" при изменении медианы - так и не разобрался, кое-как приткнул
Дело не столько в скорости сколько в "перспективах" данного кода. Случись что - и разобраться другому программисту будет трудно (если вообще возможно). Вероятно придется все снести и писать с нуля.
0
698 / 572 / 75
Регистрация: 20.09.2014
Сообщений: 3,699
19.01.2025, 18:39
Цитата Сообщение от Igor3D Посмотреть сообщение
Правду сказать, как пересчитывать "k" при изменении медианы - так и не разобрался, кое-как приткнул
Попробуйте моё описание почитать:

Цитата Сообщение от Mikhaylo Посмотреть сообщение
Вопрос №4. Как вычислять отклонение от медианы для нарастающей последовательности? В общем случае это делается так: для тех чисел, которые больше медианы, отклонение равно (A[i] - b), где b - наше медианное число, для чисел, которые меньше медианы, отклонение равно (b - A[j]). Итого сумма отклонений равна для нечётного числа элементов sum{(A[i] - b)} + sum{(b - A[j])}. Так как чисел слева и справа от медианы равное количество, то b сокращается и остаётся sum{A[i]} - sum{A[j]}. Для чётного числа элементов следует не забыть про отклонение самих медианных чисел, поэтому появляется дополнительная сумма в конце выражения sum{A[i]} - sum{A[j]} + B[m/2] - B[m/2-1] (формула для возрастающей последовательности B).
Думаю, понятно, как при наращивании последовательности наращивать такую сумму? Это чуть сложнее, чем кажется. Нужно очень аккуратно изучить вопрос, как делать безошибочные пересчёты сумм при изменении чётности числа m.
Задачка интересная, заставит подумать.
Добавлено через 12 минут
Например, нечётное число элементов [3, 5, 6, 7, 9]
median([3, 5, 6, 7, 9]) = 6
Отклонение от медианы: (6-3) + (6-5) + (6-6) + (7-6) + (9-6)
Можно посчитать "в лоб", но так неинтересно. Обратим внимание, что саму медиану можно не учитывать, он всегда самоликвидируется.
Чисел до медианы и после медианы всегда одинаковое. Это значит, что числа "6" можно поголовно сократить.
(6-3) + (6-5) + (6-6) + (7-6) + (9-6) = - 3 - 5 + 7 + 9.
То есть надо просуммировать числа после медианы и вычесть из них все числа до медианы.
(7 + 9) - (3 + 5)

Например, чётное число элементов [3, 5, 6, 7, 8, 9]
median([3, 5, 6, 7, 9]) = [6, 7] -> 6 (я беру меньшую из медиан)
Отклонение от медианы: (6-3) + (6-5) + (6-6) + (7-6) + (8-6) + (9-6) = (8 + 9) - (3 + 5) + (7 - 6)
В конце добавилась разница медиан.
0
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
19.01.2025, 23:04
Я переписал на использование дерева для хранения окна.

Использовал декартово дерево, как самое простое. Алгоритмы добавления/удаления из дерева выбрал самые простые, а не самые оптимальные.

Результат (на стареньком ноутбуке):
Code
1
2
Result = 80134
Time = 61 ms
Можно ещё заменить массив на поток (stream), чтобы не расходовать столько памяти. Возможно, это даст небольшое ускорение, но не факт.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
void Main()
{
    var path = @"c:\data\in.txt";
    var lines = File.ReadAllLines(path);
    var k = lines[0].Split(' ').Select(x => int.Parse(x)).Skip(1).First();
    var arr = lines[1].Split(' ').Select(x => int.Parse(x)).ToArray();
 
    var sw = new Stopwatch();
    sw.Start();
    var res = Solve(arr, k);
    sw.Stop();
    Console.WriteLine($"Result = {res}");
    Console.WriteLine($"Time = {sw.ElapsedMilliseconds} ms");
}
 
public int Solve(int[] numbers, int k)
{
    var maxWidth = 1;
    var w = new Treap(numbers[0], 0);
    (int Low, int High) medians = (numbers[0], numbers[0]);
    var stepCount = 0;
 
    for (int i = 1, j = 0; i < numbers.Length;)
    {
        if (stepCount > k)
        {
            var n = numbers[j++];
            Treap.Remove(ref w, n);
            Contraction(n);
            continue;
        }
 
        if (w.Width > maxWidth)
        {
            maxWidth = w.Width;
        }
 
        {
            var n = numbers[i++];
            Treap.Add(ref w, n, i);
            Expansion(n);
        }
    }
    return maxWidth;
 
    void Contraction(int n)
    {
        if (w.Width % 2 == 1)
        {
            if (n >= medians.High)
            {
                medians = (medians.Low, medians.Low);
                stepCount -= n - medians.Low;
            }
            else if (n <= medians.Low)
            {
                medians = (medians.High, medians.High);
                stepCount -= medians.High - n;
            }
            else
            {
                throw new InvalidProgramException("not possible");
            }
        }
        else
        {
            var mOld = medians.High; // w.Medians.High == w.Medians.Low
            if (n > mOld)
            {
                var low = w.OrdinalStat(w.Width / 2 - 1);
                medians = (low, mOld);
                stepCount += mOld - n;
            }
            else if (n < mOld)
            {
                var high = w.OrdinalStat(w.Width / 2);
                medians = (mOld, high);
                stepCount += n - mOld;
            }
            else
            {
                var low = w.OrdinalStat(w.Width / 2 - 1);
                var high = w.OrdinalStat(w.Width / 2);
                medians = (low, high);
                //w.StepCount = w.StepCount;
            }
        }
    }
 
    void Expansion(int n)
    {
        if (w.Width % 2 == 1)
        {
            if (n >= medians.High)
            {
                medians = (medians.High, medians.High);
                stepCount = stepCount + (n - medians.High);
            }
            else if (n <= medians.Low)
            {
                medians = (medians.Low, medians.Low);
                stepCount = stepCount + (medians.Low - n);
            }
            else
            {
                medians = (n, n);
                //w.StepCount = w.StepCount;
            }
        }
        else
        {
            var mOld = medians.High; // w.Medians.High == w.Medians.Low
            if (n > mOld)
            {
                var high = w.OrdinalStat(w.Width / 2);
                medians = (mOld, high);
                stepCount = stepCount + (n - mOld);
            }
            else if (n < mOld)
            {
                var low = w.OrdinalStat(w.Width / 2 - 1);
                medians = (low, mOld);
                stepCount = stepCount + (mOld - n);
            }
            else
            {
                medians = (n, n);
                //w.StepCount = w.StepCount;
            }
 
        }
    }
 
}
 
public class Treap
{
    private int Key;
    private int Prior;
    private int Size;
    
    private Treap Left;
    private Treap Right;
    
    public Treap(int key, int prior, Treap left = null, Treap right = null)
    {
        Key = key;
        Prior = prior;
        Left = left;
        Right = right;
        UpdateSize();
    }
    
    public int Width => Size;
    
    private void UpdateSize()
    {
        Size = (Left?.Size ?? 0) + (Right?.Size ?? 0) + 1;
    }
    
    private static void Split(Treap t, int key, ref Treap l, ref Treap r)
    {
        if (t == null)
        {
            l = r = null;
            return;
        }
        else if (t.Key <= key)
        {
            Split(t.Right, key, ref t.Right, ref r);
            l = t;
        }
        else
        {
            Split(t.Left, key, ref l, ref t.Left);
            r = t;
        }
        t.UpdateSize();
    }
    
    private static void Merge(ref Treap t, Treap l, Treap r)
    {
        if (l == null || r == null)
        {
            t = l ?? r;
        }
        else if (l.Prior > r.Prior)
        {
            Merge(ref l.Right, l.Right, r);
            t = l;
        }
        else
        {
            Merge(ref r.Left, l, r.Left);
            t = r;
        }
        t?.UpdateSize();
    }
    
    public static void Add(ref Treap t, int key, int prior)
    {
        var it = new Treap(key, prior);
        if (t == null)
        {
            t = it;
            return;
        }
        else if (t.Prior < it.Prior)
        {
            Split(t, it.Key, ref it.Left, ref it.Right);
            t = it;
        }
        else if (it.Key < t.Key)
        {
            Add(ref t.Left, key, prior);
        }
        else
        {
            Add(ref t.Right, key, prior);
        }
        t.UpdateSize();
    }
 
    public static void Remove(ref Treap t, int key)
    {
        if (t.Key == key)
        {
            Merge(ref t, t.Left, t.Right);
            return;
        }
        else if (key < t.Key)
        {
            Remove(ref t.Left, key);
        }
        else
        {
            Remove(ref t.Right, key);
        }
        t.UpdateSize();
    }
    
    public int OrdinalStat(int k)
    {
        var curr = this;
        while(curr != null)
        {
            var sizeLeft = curr.Left?.Size ?? 0;
            if (sizeLeft == k) return curr.Key;
            if (sizeLeft < k)
            {
                curr = curr.Right;
                k -= sizeLeft + 1;
            }
            else
            {
                curr = curr.Left;
            }
        }
        throw new IndexOutOfRangeException();       
    }
 
    public override string ToString()
    {
        return $"Key={Key}, Size={Size}, Prior={Prior}";
    }
 
}
0
20.01.2025, 01:46

Не по теме:

единственное, что я понял: данная задача очень просто и лаконично звучит, но вот решается совсем непросто, ну, судя по кол-ву обсуждений...ну и ладно, пойду чаек заварю...

0
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
20.01.2025, 11:52
Я пробовал использовать SortedSet, который является деревом и обещает O(log n) для всех операций. Но программа неожиданно работает примерно как O(n2). 3ms - 270ms - 37sec для n = 1000 - 10000 - 100000. Ответ выдаёт правильный.

Код практически тот же самый:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
public int Solve(int[] numbers, int k)
{
    var maxWidth = 1;
    var w = new SortedSet<(int,int)>();
    w.Add((numbers[0], 0));
    (int Low, int High) medians = (numbers[0], numbers[0]);
    var stepCount = 0;
 
    for (int i = 1, j = 0; i < numbers.Length;)
    {
        if (stepCount > k)
        {
            var n = numbers[j];
            w.Remove((n,j++));
            Contraction(n);
            continue;
        }
 
        if (w.Count > maxWidth)
        {
            maxWidth = w.Count;
        }
 
        {
            var n = numbers[i];
            w.Add((n, i++));
            Expansion(n);
        }
    }
    return maxWidth;
 
    void Contraction(int n)
    {
        if (w.Count % 2 == 1)
        {
            if (n >= medians.High)
            {
                medians = (medians.Low, medians.Low);
                stepCount -= n - medians.Low;
            }
            else if (n <= medians.Low)
            {
                medians = (medians.High, medians.High);
                stepCount -= medians.High - n;
            }
            else
            {
                throw new InvalidProgramException("not possible");
            }
        }
        else
        {
            var mOld = medians.High; // w.Medians.High == w.Medians.Low
            if (n > mOld)
            {
                var low = w.ElementAt(w.Count / 2 - 1).Item1;
                medians = (low, mOld);
                stepCount += mOld - n;
            }
            else if (n < mOld)
            {
                var high = w.ElementAt(w.Count / 2).Item1;
                medians = (mOld, high);
                stepCount += n - mOld;
            }
            else
            {
                var low = w.ElementAt(w.Count / 2 - 1).Item1;
                var high = w.ElementAt(w.Count / 2).Item1;
                medians = (low, high);
                //w.StepCount = w.StepCount;
            }
        }
    }
 
    void Expansion(int n)
    {
        if (w.Count % 2 == 1)
        {
            if (n >= medians.High)
            {
                medians = (medians.High, medians.High);
                stepCount = stepCount + (n - medians.High);
            }
            else if (n <= medians.Low)
            {
                medians = (medians.Low, medians.Low);
                stepCount = stepCount + (medians.Low - n);
            }
            else
            {
                medians = (n, n);
                //w.StepCount = w.StepCount;
            }
        }
        else
        {
            var mOld = medians.High; // w.Medians.High == w.Medians.Low
            if (n > mOld)
            {
                var high = w.ElementAt(w.Count / 2).Item1;
                medians = (mOld, high);
                stepCount = stepCount + (n - mOld);
            }
            else if (n < mOld)
            {
                var low = w.ElementAt(w.Count / 2 - 1).Item1;
                medians = (low, mOld);
                stepCount = stepCount + (mOld - n);
            }
            else
            {
                medians = (n, n);
                //w.StepCount = w.StepCount;
            }
 
        }
    }
 
}
Добавлено через 52 минуты
Выяснилось, что в SortedSet нет метода для получения элемента по индексу. Используется метод из IEnumerable, который O(n).
0
1963 / 819 / 114
Регистрация: 01.10.2012
Сообщений: 4,769
Записей в блоге: 2
20.01.2025, 12:48
Цитата Сообщение от Shamil1 Посмотреть сообщение
Выяснилось, что в SortedSet нет метода для получения элемента по индексу. Используется метод из IEnumerable, который O(n).
На плюсах аналогично, std::distance не работает с деревом хорошо. Я использовал одну "левую" медиану ((tree.size() - 1) / 2). Поддерживать ее (без индекса) при вставках/удалениях несложно. Но трудным (для меня) оказался пересчет "k" (stepCount). Если медиана осталась той же - просто добавляем разницу эл-та с ней. Но вот если медиана изменилась.. В конце-концов я устал от аналитики и добавил "прямой" пересчет после каждого шага, и в тех ветках где рез-т неверен добавил разницу медиан. Почему-то в Вашем коде такой проблемы нет
0
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
20.01.2025, 14:26
Цитата Сообщение от Igor3D Посмотреть сообщение
Почему-то в Вашем коде такой проблемы нет
Потому что у меня две медианы. И одна из них всегда будет новой медианой.
0
1963 / 819 / 114
Регистрация: 01.10.2012
Сообщений: 4,769
Записей в блоге: 2
20.01.2025, 16:00
Цитата Сообщение от Shamil1 Посмотреть сообщение
Потому что у меня две медианы. И одна из них всегда будет новой медианой.
Не могу уловить, какую разницу добавлять/отнимать в stepCount: с новой медианой или со старой?
0
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
20.01.2025, 16:21
Цитата Сообщение от Igor3D Посмотреть сообщение
Я использовал одну "левую" медиану ((tree.size() - 1) / 2). Поддерживать ее (без индекса) при вставках/удалениях несложно.
В C# в SortedSet у итератора есть только MoveNext. Поэтому такой способ не подойдёт.

Цитата Сообщение от Igor3D Посмотреть сообщение
какую разницу добавлять/отнимать в stepCount: с новой медианой или со старой?
Цитата Сообщение от Igor3D Посмотреть сообщение
Если медиана осталась той же - просто добавляем разницу эл-та с ней. Но вот если медиана изменилась..
При пересчёте можно считать, что медиана осталось прежней. То есть, использовать для пересчёта ту медиану (из двух), которая осталась медианой.
0
698 / 572 / 75
Регистрация: 20.09.2014
Сообщений: 3,699
20.01.2025, 19:25
Две медианы ищутся за время O(1), нет необходимости их помнить, при чём на 50% зря.)))
0
Модератор
Эксперт функциональных языков программирования
3132 / 2279 / 469
Регистрация: 26.03.2015
Сообщений: 8,870
20.01.2025, 20:08
Цитата Сообщение от Mikhaylo Посмотреть сообщение
Две медианы ищутся за время O(1)
Как?
0
698 / 572 / 75
Регистрация: 20.09.2014
Сообщений: 3,699
20.01.2025, 20:45
[3, 5, 6, 7, 8, 9]
Вот же они по середине. Индекс первой медианы 6/2-1 = 2, индекс второй медианы 6/2 = 3 (индексы начинаются с нуля).
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
20.01.2025, 20:45
Помогаю со студенческими работами здесь

Элементы массива уменьшить на 20, умножить на последний элемент и увеличить на число В
Помогите решить задачу, пожалуйста. Сама задача: &quot;Дан массив. Все его элементы уменьшить на 20, затем умножить на последний элемент,...

Элементы массива: увеличить в 2 раза, уменьшить на число А, разделить на первый элемент
Дан массив. Все его элементы: а) увеличить в 2 раза; б) уменьшить на число А; в) разделить на первый элемент.

Если последний элемент массива положителен, то все элементы увеличить на квадрат максимума всего массива
4 Задан одномерный массив F(N). Если последний элемент массива положителен, то все элементы увеличить на квадрат ...

Если последний элемент массива положителен, то все элементы увеличить на квадрат максимума всего массива
Задан одномерный массив F(N). Если последний элемент массива положителен, то все элементы увеличить на квадрат максимума всего...

Найти максимальный по значению элемент массива и увеличить его в два раза, остальные элементы уменьшить на значение минимума последней строки массива
Ввести двумерный массив A (NxM), вывести его. Найти максимальный по значению элемент массива и увеличить его в два раза. Все остальные...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru