На форумі обговорюються лише питання, пов'язані з олімпіадою
Ви не зайшли.
Почну нову тему, хоч il____ уже й почав викладати рішення в існуючих. По-моєму, зручніше буде, коли обговорення рішень проходить окремо від обговорення задач.
Викладаю код уже перевірених задач, поступово додаватиму коментарі.
SORTING
Загальна ідея:
назвемо "циклом" таку підмножину даної послідовності, що за будь-якої послідовності обмінів, кожен обмін двох елементів циклу ставитиме на "своє" місце не більше, ніж 1 число. Очевидно, що для сортування кожного циклу потрібно k-1 обмін, де k - кількість елементів циклу.
Якщо у нас m таких циклів, то відповіддю буде sum{i=1;m}(k_i-1)=sum{i=1;m}(k_i)-m;
Оскільки, sum{i=1;m}(k_i)=n, то відповіддь перетворюється на n-m. Де n - к-ть елементів у масиві, m - к-ть "циклів";
#include <vector>
#include <iostream>
using namespace std;
int main()
{
int N,t,res;
cin>>N;
res=N;
vector<int> a(N);
for(int i=0;i<N;i++)cin>>a[i],a[i]--;
for(int i=0;i<N;i++)
{
if(a[i]==-1)continue;
t=i;
do
{
int T=t;
t=a[t];
a[T]=-1;
}while(t!=i&&t>=0);
res--;
}
cout<<res<<endl;
}SEQUENCE
Загальна ідея:
Позначимо за х шукане число. Тоді для пошуку ікса можна скласти квадратне рівняння. Після певної кількості перетворень сум отримаємо рівняння
x^2-2k^2 * x - k^2 * (2k+1).
Коренями цього рівняння будуть -k та k(2k+1)=k*n=n*(n-1)/2. Перший нам не підходить, а другий завжди задовольнятиме умові. Тобто відповіді "-1" бути не може.
#include <iostream>
using namespace std;
int main()
{
int N;
cin>>N;
cout<<N*(N-1)/2<<endl;
}PLUMS
Підійде будь-який алгоритм генерації магічних квадратів, описаний тут, оскільки у нашому квадраті умова про рівність сум має виконуватися тільки для рядків.
#include <vector>
#include <iostream>
using namespace std;
class almostMagic
{
int N;
vector<vector<int> > a;
public:
void init()
{
int x=0,y=N/2,cnt=1;
while(cnt<=N*N)
{
if(x<0){x=N-1;continue;}
if(y<0){y=N-1;continue;}
if(x>=N){x=0;continue;}
if(y>=N){y=0;continue;}
if(a[x][y]){x--;continue;}
a[x][y]=cnt;
cnt++;
x--;y++;
}
}
almostMagic(int n=0):N(n),a(n,vector<int>(n)){init();}
friend istream& operator>>(istream &in,almostMagic& rhs)
{
in>>rhs.N;
rhs.a.resize(rhs.N,vector<int>(rhs.N));
rhs.init();
}
friend ostream& operator<<(ostream& out,almostMagic& rhs)
{
for(vector<vector<int> >::iterator i=rhs.a.begin();i!=rhs.a.end();++i)
{
for(vector<int>::iterator j=i->begin();j!=i->end();++j)
out<<*j<<" ";
out<<endl;
}
}
};
int main()
{
almostMagic a;
cin>>a;
cout<<a;
}SumOfPowers
Відправив на перевірку версію з використанням STL, яка при використанні оптимізації компілятора -О2 працює 200мс на найгіршому тесті, але без оптимізації, на жаль, майже 2 секунди. Оскільки тут, як я нещодавно дізнався, оптимізації вимкнені, то я отримав ТЛ-и на кількох тестах и отримав результат 33 бали(
Викладаю модифіковану, менш чуттєву до прапорів компіляції. Набирає 40 балів.
Загальна ідея:
Задачу можна звести до задачі про банкомат з нескінченною кількістю банкнот. "Номіналами" виступатимуть чила виду i^K. Така задача розв’язується за допомогою ДП з асимптотикою O(N*L), де N - максимальне вхідне число, L - кількість номіналів. Оскільки номіналів у нас N^(1/K), то маємо асимптотику O(N(1+1/K)).
І все було б добре, якби не квадрати. Для них асимтотика O(N^1.5) працює занадто довго.
Можна помітити, що будь-яке число можна розкласти на суму не більше, ніж 4 квадратів. По моєму, це навіть кимось доведено математично. Скористаємося цим фактом. Можна було б розкладати кожне число(перевірити, чи є число квадратом можна за О(1), розкласти на 2 квадрати - за O(sqrt(N), на 3 квадрати - за O(N). Якщо розкладання не знайдено, то відповідь - 4). Але в такому разі асимптотика була б O(M*N), що при даних обмеженнях може бути навіть гірше ніж початкове O(N^1.5). Тому не будемо повністю "викидати" динаміку, адже ми все-одно неодноразово вирішуємо однакові підзадачі, що перекриваються. Заповнимо наш "банкоматний" масив тілько одиницями(O(sqrt(N)) та двійками (O(N)). далі розглядаємо в циклі всі наші числа, і якщо це число ще не розкладене на 1 чи 2 квадрати - пробуємо відняти від нього всі i, такі що i^2<=N. І якщо на місці різниці стоїть двійка, то відповідь 3. Якщо жодного разу такої ситуації не виникло - відповідь 4.
Таким чином, для квадратів отримали асимптотику O(N+M*sqrt(N))
Ну і ще деякі незначні оптимізації та хаки для крайніх випадків(при К=1, очевидно, відповідь для будь-кого числа - 1. А при К>=20 при даних обмеженнях відповіддю для будь-якого числа буде саме це число, оскільки 2^20>987654).
#include <iostream>
#include<algorithm>
#include<limits>
using namespace std;
typedef unsigned long long ull;
ull fast_pow(ull x,ull power)
{
ull res=1;
while(power)
{
if(power%2)res*=x;
x*=x;
power>>=1;
}
return res;
}
int main()
{
int K,M,max=numeric_limits<int>::min();
cin>>K>>M;
static int a[9876];
for(int i=0;i<M;i++)cin>>a[i],max=std::max(max,a[i]);
if(K>=20)
{
for(int i=0;i<M;i++)cout<<a[i]<<" ";
return 0;
}
if(K==1)
{
for(int i=0;i<M;i++)cout<<1<<" ";
return 0;
}
static int powers[1000],result[987656];
memset(result,0x7f,sizeof(result));
result[0]=0;
ull t;
for(int i=1;;i++)
{
t=fast_pow(i,K);
powers[i-1]=t;
if(t>max)break;
result[t]=1;
result[t+1]=2;
}
if(K==2)
{
for(int k=0;powers[k]<=max;k++)
for(int j=k;powers[j]+powers[k]<=max;j++)
if(result[powers[j]+powers[k]]!=1)result[powers[j]+powers[k]]=2;
for(int i=0;i<M;i++)
{
int res=result[a[i]]<=3?result[a[i]]:4;
for(int k=0;powers[k]<=a[i]&&res==4;k++)
{
if(result[a[i]-powers[k]]==2)res=3;
}
result[a[i]]=res;
cout<<res<<" ";
}
}
else
{
for(int i=2;i<=max;i++)
{
for(int j=0;powers[j]<=i/2;j++)
{
if(result[i]<=2)break;
if(result[i-powers[j]]+1<result[i])result[i]=result[i-powers[j]]+1;
}
}
for(int i=0;i<M;i++)
cout<<result[a[i]]<<" ";
}
}Platforms
Як і багато інших, я провтикав можливість стрибати назад і отримав 12 балів. Як і у багатьох інших, після додавання однієї умови задача пройшла на 40 балів.
Принцип роботи пояснювати, думаю, не має сенсу - банальна динаміка. Але якщо комусь потрібно все-таки описати алгоритм, то опишу)
#include <vector>
#include <iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main()
{
int N;
cin>>N;
vector<ll> a(N),b(N),c(N);
for(int i=0;i<N;i++)cin>>a[i];
b[0]=c[0]=0;
b[1]=llabs(a[1]-a[0]);
c[1]=(a[1]-a[0])*(a[1]-a[0]);
for(int i=2;i<N;i++)
{
b[i]=min(b[i-1]+llabs(a[i]-a[i-1]),b[i-2]+3*llabs(a[i]-a[i-2]));
c[i]=min(c[i-1]+(a[i-1]-a[i-2])*(a[i-1]-a[i-2])+3*(a[i]-a[i-2])*(a[i]-a[i-2]),min(c[i-1]+(a[i]-a[i-1])*(a[i]-a[i-1]),c[i-2]+3*(a[i]-a[i-2])*(a[i]-a[i-2])));
}
cout<<b.back()<<" "<<c.back()<<endl;
}Відредаговано Dim_ov (2011-12-30 12:47:15)
Поза форумом
(И.Н. Порублёв А.Б. Ставровский Алгоритмы и программы решение олимпиадных задач)
вроде этот код посимпатичнее будет... ![]()
//Plums
#include <iostream>
using namespace std;
int main() {
int N,NN;
cin >> N;
NN=N*N;
for(int i=1,n; i<=N; i++)
{
n=i;
cout << n;
while(n<=NN)
{
if (n%N == 0) n++; else n=n+N+1;
if (n<=NN) cout<<" "<<n;
}
cout << endl;
}
return 0;
}А задачу SumOfPowers на 40 баллов решило всего 10 человек!
Интересно, поделится кто-нибудь решением, или нет?
Відредаговано LVV (2011-12-30 10:47:01)
Поза форумом
Platforms - 40 : (после того, как узнал про вариант прыжка назад, добавил 2 строчки и сдал
)
#include <iostream>
using namespace std;
long long MINIMUM(long long a,long long b){
if(a<b)return a;
else return b;
}
long long A(long long z){
return z>0 ? z : -z ;
}
main(){
long long a[50001],b[50001],c[50001];
int i,n;
cin>>n;
for(i=1;i<=n;i++)cin>>a[i];
b[0]=0;
b[1]=0;
b[2]=A(a[2]-a[1]);
c[0]=0;
c[1]=0;
c[2]=(a[2]-a[1])*(a[2]-a[1]);
for(i=3;i<=n;i++){
long long b1,b2; //два варианта, чтоб не запутаться (1 задача)
long long c1,c2,c3; //три варианта, чтоб не запутаться (2 задача)
b1=A( a[i]-a[i-1] )+b[i-1]; //вариант прыжка с соседней платформы
b2=A( 3*(a[i]-a[i-2]) )+b[i-2]; //вариант прыжка через платформу
b[i]=MINIMUM(b1,b2);
c1=(a[i]-a[i-1])*(a[i]-a[i-1])+c[i-1]; //вариант прыжка с соседней платформы
c2=3*(a[i]-a[i-2])*(a[i]-a[i-2])+c[i-2]; //вариант прыжка через платформу
c3=( (a[i-1]-a[i-2])*(a[i-1]-a[i-2])+c[i-1] ) + 3*(a[i]-a[i-2])*(a[i]-a[i-2]); //вариант прыжка с соседней назад, и через платформу
c[i]=MINIMUM( MINIMUM(c1,c2),c3 );
}
cout<<b[n]<<" "<<c[n];
}Відредаговано Unknown (2011-12-29 13:37:32)
Поза форумом
Щодо SumOfPowers
Основний алгоритм такий.
Заводимо одновимірний масив DP[] від 0 до maxN (максимального зі згаданих у вхідних даних Ni).
Ініціалізуємо його як DP[i]=i, тому що яким би не був показник K завжди можна набрати i як суму i штук 1^K. Далі перебираємо всі такі основи j, що j^K <= maxN, і для кожного з них дивимося, чи може використання такого степеню зменшити кількість доданків:
DP[i + j^K] = min(DP[i + j^K] , DP[i]+1), де i знов-таки перебираються всі можливі.
Після завершення цих (вкладених) циклів, просто виводимо для кожного вхідного Ni відповідний елемент DP[Ni].
АЛЕ!!! Хоча описаний алгоритм формально правильний для всіх можливих вхідних даних, фактично його доцільно використовувати виключно при 3<=K<=19.
При K>=20 відповідь для будь-якого Ni все одно гарантовано буде Ni, тому що навіть 2^K вже більше за Ni.
При K=1 цей алгоритм працюватиме дуже довго, вертаючи для будь-якого Ni гарантовану відповідь 1, бо будь-яке Ni можна подати у вигляді одного доданку Ni^1
Найменш очевидний момент -- при K=2 алгоритм теж працює занадто довго. Треба було знайти у літературі/Інтернеті/ще_де-небудь факт, що будь-яке натуральне число може бути подане у вигляді суми не більш як чотирьох квадратів натуральних чисел, а також ознаки, для яких саме чисел ця кількість буде 1, для яких 2, для яких 3 і для яких 4.
І для K=1 та K=2 доцільно НЕ заповнювати табличку DP, а просто розв"язувати окремо для кожного введеного Ni.
Можливо, в якійсь невеликій мірі все це (різні алгоритми при різних K) і брудний хак. Але, на моє тверде переконання, лише в зовсім невеликій. У значно меншій, ніж, наприклад, масив констант.
Поза форумом
Ilya Porublyov написав:
Можливо, в якійсь невеликій мірі все це (різні алгоритми при різних K) і брудний хак. Але, на моє тверде переконання, лише в зовсім невеликій. У значно меншій, ніж, наприклад, масив констант.
До речі, журі не відстежували, скільки було спроб таки відправити мегабайти захардкоджених відповідей? ![]()
Поза форумом
Dim_ov написав:
то маємо асимптотику O(N(1+1/K))
А если k=3? Ведь тогда асимптотика N*N^{1/3} = 10^6 * 10^2 = 10^8. Многовато, разве нет? Или уже решения, которые делают 10^8 операций, принимаются? (безусловно сейчас на неплохих компьютерах/серверах такие решения влазят в 1с, но...).
Лично у меня было написано такое же решение, которое было описано выше Ilya Porublyov, но я подумал, что отправив его, для небольших k в ТЛ оно не влезит.
Может кто-то подсказать, почему код:
#include <iostream>
using namespace std;
int main()
{
int k, m, n;
scanf("%d%d", &k, &m);
for (int i=0; i<m; (i != (m-1)) ? printf(" ") : printf("\n"), ++i) {
scanf("%d", &n);
if (k >= 20) printf("%d", n); else
if (k >= 13) printf("%d", (n/(1<<k)) + (n%(1<<k))); else
if (k == 1) printf("1"); else
printf("0");
}
}
на некоторых тестах работает больше секунды и получает ТЛ (на онлайн проверке)?
Відредаговано Зевс (2011-12-29 20:16:43)
Поза форумом
Зевс написав:
А если k=3? Ведь тогда асимптотика N*N^{1/3} = 10^6 * 10^2 = 10^8. Многовато, разве нет? Или уже решения, которые делают 10^8 операций, принимаются? (безусловно сейчас на неплохих компьютерах/серверах такие решения влазят в 1с, но...).
Лично у меня было написано такое же решение, которое было описано выше Ilya Porublyov, но я подумал, что отправив его, для небольших k в ТЛ оно не влезит.
Ну, я вирішив не гадати, влізе чи не влізе, а просто спробувати
І працює воно ~400ms без оптимізацій та ~200ms з -О2. ДП для квадратів працювало, якщо не помиляюся, близько 2-3 секунд, тобто удесятеро довше, ніж для кубів. Що, в принципі й логічно (10^9/10^8=10, зі збільшенням К час зменшується експоненціально). Ну і комп’ютер у мене не самий швидкий(2.33 ГГц). Так що в секунду влазить із запасом навіть на не найсучаснішому сервері.
Чому код отримує ТЛ-и навіть не уявляю ![]()
UPD: Схоже причина - перемішування введення та виведення. Якщо читати в масив, а потім опрацьовувати - то без ТЛ-ів. Мабуть різні тести по-різному вводяться перевіряючою системою в програму.
Відредаговано Dim_ov (2011-12-29 21:31:01)
Поза форумом
Хм... я чогось думав що scanf і printf не в iostream, а в stdio.h
у мене цей код навіть не компілиться... а север приймає... і перевіряє
Дивина...
Поза форумом
Dim_ov написав:
Оновив шапку
В Visual Studio 2010 Ваш код SumOfPowers не компилируется. ![]()
в
ull fast_pow(ull x,ull power)
ull - не определён. ![]()
Відредаговано LVV (2011-12-30 11:09:39)
Поза форумом
Ilya Porublyov написав:
Щодо SumOfPowers
Основний алгоритм такий...........
Ну, с алгоритмом более-менее понятно.
А нельзя ли увидеть код рабочей программы.
Просто я был уверен в правильности свого решения, но полный набор тестов оно не прошло. И не попричине тайм-лимита, а по причине неверных результатов.
Вот и хотелось бы сравнить и проверить кое-что.
Поскольку полные наборы тестов являются секретом до самого завершения олимпиады, то хотелось бы всё таки хоть один рабочий код полнобальной программы SumOfPowers увидеть, чтобы Новый год встретить спокойно ![]()
С наступающим! ![]()
Поза форумом
Зевс написав:
Может кто-то подсказать, почему код:
#include <iostream>
using namespace std;
int main()
{
int k, m, n;
scanf("%d%d", &k, &m);
for (int i=0; i<m; (i != (m-1)) ? printf(" ") : printf("\n"), ++i) {
scanf("%d", &n);
if (k >= 20) printf("%d", n); else
if (k >= 13) printf("%d", (n/(1<<k)) + (n%(1<<k))); else
if (k == 1) printf("1"); else
printf("0");
}
}
на некоторых тестах работает больше секунды и получает ТЛ (на онлайн проверке)?
MItornaDOS написав:
Хм... я чогось думав що scanf і printf не в iostream, а в stdio.h
у мене цей код навіть не компілиться... а север приймає... і перевіряє
Дивина...
У Visual Studio компілює нормально...
Але результати видає абсурдні: навіть на стандпртний тест 2 3 9 17 6 видає три нулі.
Поза форумом
LVV написав:
Але результати видає абсурдні: навіть на стандпртний тест 2 3 9 17 6 видає три нулі.
Якщо я все правильно зрозумів, результати і не претендують на правильність. Питання було у тому, чому цей код, який має працювати за О(М), видає ТЛ-и на онлайн-перевірці.
Поза форумом
Конечно даже на стандартный тест этот код выдает нули, так как это не код рабочей программы
. Мой код рабочей программы не проходит некоторые тесты по ТЛ, хотя все остальные отрабатывает правильно за 0,01с - 0,02с. И я не могу понять почему. Вот я его и упростил - конечно же теперь все тесты FAILED, только дело в том, что вот такой простой кусок кода на тех же самых тестах получает ТЛ. И не ясно, почему. Я тоже думал, что возможно дело в том, что идет поочередный ввод/вывод, но во-первых большинство-то тестов проходит. А во-вторых чем же это плохо, если программа выводит ответ сразу, а не сохраняет все ответы в массив и потом их выводит. Еще ни на одной олимпиаде, ни на одной проверяющей системе такой проблемы я не встречал. Хотя все-равно довольно странным остается то, что большинство тестов проходят.
Уважаемое Жюри, если проблема действительно в поочередном вводе/выводе, можете написать, пожалуйста, об этом. Мне хотелось бы разобраться с образовавшейся ситуации, чтобы в следующий раз не делать подобных ошибок. Заранее спасибо.
Поза форумом
LVV
що до SumOfPowers..
введи 3 1 32
що виводить?
Поза форумом
shadybaby написав:
LVV
що до SumOfPowers..
введи 3 1 32
що виводить?
Да Вы прямо мои мысли читаете. ![]()
Действительно:
32 = 2^3+2^3+2^3+2^3 (4 слагаемых)
А у меня не так.
Выходит, мой алгоритм никудышний, хоть и набрал 11 баллов...)
Спасибо за пример.
С Наступающим!
Відредаговано LVV (2011-12-31 06:51:58)
Поза форумом
Мій SumOfPowers, надіслав не ту версію і втратив 3 бали ![]()
#include <iostream>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#define MAX_N 1000001
using namespace std;
int mpow (long long a, long long n)
{
long long p = 1 % n;
while (n)
{
if (n % 2)
p = p * a;
if (n /= 2)
a = a * a;
if (p > MAX_N)
return MAX_N;
}
return p;
}
int d[MAX_N], dp[1001], sv[10000], n, m, k, qp, i, j;
int main()
{
cin >> k >> n;
for (i = 0; i < n; i++)
cin >> sv[i];
if (k == 1)
{
cout << '1';
for (i = 1; i < n; i++)
cout << " 1";
cout << endl;
// return 0; -3 бали тут :(
}
if (k == 2)
{
char s2[MAX_N] = {}, s3[MAX_N] = {};
for (i = 1; i <= 1000; i++)
dp[i] = i*i;
for (i = 1; i <= 1000; i++)
for (j = 1; dp[i] + dp[j] < MAX_N; j++)
s2[dp[i] + dp[j]] = 1;
for (i = 0; i < n; i++)
{
d[sv[i]] = 4;
int sq = sqrt(double(sv[i]));
if (sq * sq == sv[i])
{
d[sv[i]] = 1;
continue;
}
if (s2[sv[i]])
{
d[sv[i]] = 2;
continue;
}
d[sv[i]] = 4;
for (j = 1; j*j <= sv[i]; j++)
if (s2[sv[i] - j*j])
{
d[sv[i]] = 3;
continue;
}
}
cout << d[sv[0]];
for (i = 1; i < n; i++)
cout << ' ' << d[sv[i]];
cout << endl;
return 0;
}
m = 0;
for (i = 1; dp[i-1] < MAX_N; i++)
dp[i] = mpow(i, k), m++;
d[0] = 0;
for (i = 1; i < MAX_N; i++)
{
d[i] = i;
for (j = 1; dp[j] <= i; j++)
{
qp = d[i - dp[j]] + 1;
if (d[i] > qp)
d[i] = qp;
}
}
cout << d[sv[0]];
for (i = 1; i < n; i++)
cout << ' ' << d[sv[i]];
cout << endl;
return 0;
}Поза форумом
iama написав:
втратив 3 бали
Можете сказать на каких примерно тестах потеряли? Просто потерял примерно столько, сколько и вы
Поза форумом
Unknown написав:
iama написав:
втратив 3 бали
Можете сказать на каких примерно тестах потеряли? Просто потерял примерно столько, сколько и вы
Тесты, в которых k = 1, получил RE из-за того, что забыл прерывать выполнение после вывода правильного ответа. Если раскомментировать return 0; - будет заходить в полный балл.
Відредаговано iama (2011-12-31 18:53:12)
Поза форумом
iama написав:
Тесты, в которых k = 1, получил RE из-за того, что забыл прерывать выполнение после вывода правильного ответа. Если раскомментировать return 0; - будет заходить в полный балл.
Ясно, я не из-за этого точно ![]()
Поза форумом
Когда будет работать онлайн-проверка? Сейчас выдает "Произошла техническая ошибка"
Поза форумом
Дякую, я був неуважний
Поза форумом
Якщо в умові задачі Sorting "є певна надлишковість", то в умові Platforms є певна, на мою думку, недосказанність.
Вважаючи умову Platforms дуже вдалою, я все таки хотів би зауважити, що хоч стрибки назад і можна вважати вдалою родзинкою задачі, то це лише тому, що ніхто на форумі не поставив питання щодо можливості таких стрибків. А якби таке питання було, то автори (чи журі) були б зобов'язані дати на нього вичерпну відповідь. І тоді ця "таємниця" перестала би бути таємницею. А значить учасники що вже встигли відправити свої рішення опинились би в гірших умовах, ніж ті учасники, що ще не зробили цього (і сдідкують за форумом).
Доречі, учасники, що не встигли прочитати повідомлення "Мимопроходящего", теж опинились в гірших умовах.
Хотів би також відзначити дуже велику розбіжність у складності задач ІІ туру. Порівняти хоча б задачу Sequence з Platforms чи з SumOfPowers.
А взагалі, я хочу подякувати авторам, що умови задач перших двох турів були продуманими, вичерпними і чіткими. Завдяки цому на форумі майже не "ломали списів" стосовно умов завдань, чи результатів туру.
Усіх з Новим роком. ![]()
Відредаговано LVV (2012-01-02 12:42:00)
Поза форумом