Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Koodaa kiva algoritmi

Sivun loppuun

kllp [10.12.2013 00:10:18]

#

Ajattelin tehdä tällaisen ketjun. Eli ideana, että sanotaan tehtävä ja sitten seuraavien pitää coodaa se.

Sitten voidaan myös vertailla erilaisia toteutuksia ja antaa kommentteja ja parannusehdotuksia jne. :)

Aloitan:
Coodaa segmenttipuu, johon voi laittaa yksittäisiä arvoja ja josta voi kysellä summia väleiltä.

Lisäys: Voi, voi. Viisi minuuttia mennyt ja kukaan ei ole vielä koodannut. Alan jo huolestua.

Antti Laaksonen [10.12.2013 00:18:53]

#

Tässä N tarkoittaa lukujen määrää, laita(k, x) muuttaa kohdan k luvuksi x ja summa(a, b) laskee lukujen summan välillä a..b.

#define N (1<<20)
int p[2*N]
void laita(int k, int x) {
    k += N;
    p[k] = x;
    for (k = k/2; k >= 1; k /= 2) p[k] = p[2*k]+p[2*k+1];
}
int summa(int a, int b) {
    a += N; b += N;
    int t = 0;
    while (a <= b) {
        if (a%2 == 1) {t += p[a]; a++};
        if (b%2 == 0) {t += p[b]; b--};
        a /= 2; b /= 2;
    }
    return t;
}

Seuraava tehtävä: Toteuta lyhyt, toimiva ja tehokas koodi maksimivirtauksen laskemiseen.

kllp [10.12.2013 00:19:12]

#

Ihan hyvältä koodilta vaikuttaa. Itse olisin tehnyt summa-funktion näin:

int summa(int a, int b) {
    a+=N;b+=N;
    int t=0;
    //while TÄSSÄ wtf?!
    for(;a<=b;a>>=1,b>>=1) { //eikös näillä ole OIKEASTI joku ihan OIKEA ero vai oonko ihan väärässä nyt? no ei ole oleellisin muutos koodissa kyllä
        if(a%2) t+=p[a++]; //ehkä tärkein muutos edelliseen koodiin verrattuna
        if(b%2==0) t+=p[b--];
    }
    return t;
}

Deffi [10.12.2013 00:29:28]

#

Summaongelmaan parempi ratkaisu ois käyttää taulukkoa D, missä D(0)=0 ja D(i) on lukujen a1, a2, ..., ai summa. Sitten välin i..j summan saa vakioajassa laskemalla D(j)-D(i-1). Segmenttipuu on paska.

Yksittäisten arvojen "laittaminen" jää tosin puuttumaan...

Sisuaski [10.12.2013 00:31:52]

#

#include <vector>
#include <cstring>
using namespace std;
const int N=1024;
vector<int> es[N]; // kaarten vieruslistat
int cap[N][N]; // jäännöskapasiteetit
int S,T; // alku- ja loppusolmut
bool used[N];
int dfs(int n, int f) {
  if (n==T) return f;
  used[n]=1;
  int a=0;
  for(int i: es[n]) if (!used[i] && cap[n][i] && (a=dfs(i, min(f, cap[n][i])))) {
    cap[n][i] -= a;
    cap[i][n] += a;
    return a;
  }
  return 0;
}
int flow() {
  int r=0;
  for(int a; memset(used,0,sizeof(used)), a=dfs(S, 1e9); r+=a);
  return r;
}

Tehtävä: toteuta sudokun ratkaisija.

Lisäys:
Antin laita-funktion voisi mielestäni toteuttaa mukavammin näin:

void laita(int k, int x) {
  for(x-=p[k+=N]; k; k/=2) p[k]+=x;
}

kllp [10.12.2013 00:45:10]

#

Deffi: Ite oot paska. Segmenttipuu on hienoin juttu koko koodauksessa. Ainakin 100000x hienompi kuin KEHÄJONOT ETTÄ OO HILJAA VAAN.

Sisuaskin koodi on ehkä vielä enemmän hienoin kuin segmenttipuut (jos ajatellaan ihan tuollaista normaalia kuitenkin tai siis missä no joo ehkä tajusitte) <33333333333333 Voisin katsella tuota koko yön :333

Apua ja tuo laita-funktio. IIIIIIH <333333

Lisäys:

#include <iostream>
using namespace std;
int t[9][9];
int a1[3][3],a2[9],a3[9];
bool ratkaise(int x) {
    if(x==81) return 1;
    if(t[x/9][x%9]!='.') return ratkaise(x+1); //puuttui puolipiste QAQ
    for(int i=1; i<10; ++i) {
        int q=x/27;
        int w=(x%9)/3;

        if(~(a1[q][w]|a2[x/9]|a3[x%9])&1<<i) {
            a1[q][w]|=1<<i;
            a2[x/9]|=1<<i;
            a3[x%9]|=1<<i;
            t[x/9][x%9]=i;
            if(ratkaise(x+1)) return 1;
            a1[q][w]^=1<<i;
            a2[x/9]^=1<<i;
            a3[x%9]^=1<<i;
        }
    }
    return 0;
}
int main() {
    // . jos ei tiedetä lukuu
    for(int i=0; i<9; ++i) {
        for(int j=0; j<9; ++j) {
            char q;
            cin>>q;
            if(q!='.') {
                q-='0';
                a1[i/3][j/3]|=1<<q;
                a2[i]|=1<<q;
                a3[j]|=1<<q;
            }
            t[i][j]=q;
        }
    }
    if(ratkaise(0)) {
        for(int i=0; i<9; ++i) {
            for(int j=0; j<9; ++j) {
                cout<<t[i][j]<<' ';
            }
            cout<<'\n';
        }
        cout<<'\n';
    }
}

edit1: hubs bugi

edit2: käänsin ja suoritin ja löysin bugin saankohan nyt korjaa senkin no ehkä muuten vain ärsyttäisi muita tai emt

Sisuaski [10.12.2013 01:31:49]

#

Ihan elegantti ratkaisu kllp, plussaa siitä että rekursio hoidettu yhdellä muuttujalla eikä turhaan x:ää ja y:tä erikseen.

kllp [10.12.2013 02:22:15]

#

Seuraava tehtävä:
Uolevi on ottanut osaa kukkienkasvatuskilpailuun. Hän on kuitenkin harmikseen kohdannut visaisen ongelman: muut kilpailijat yrittävät vakoilla Uolevia kasvatuspuuhissaan. Uolevi onkin päättänyt pystyttää kukkien ympärille aidan, joka pitäisi kilpailijoiden uteliaat katseet loitolla. Uoleville olisi tärkeää käyttää mahdollisimman paljon aikaa kukkien hoitoon, joten aidan pystyttämisen hän haluaisi tehdä nopeasti.

Uolevilla on myös lista kukkien kokonaislukukoordinaateista. Auta Uolevia valitsemaan aidan pylväille sellaiset pisteet, että yksikään kukka ei jää aidan ulkopuolelle (ajattellaan, että äärettömän lähelle kukkaa voi pystyttää aidan). Laudat pylväiden välissä ovat aina suoria. Tiedetään myös, että Uolevi pystyy pystyttämään aitaa tietyn määrän aikayksikössä. Aidan pitäisi siis olla lisäksi mahdollisimman lyhyt.

The Alchemist [10.12.2013 11:40:26]

#

Vastoin kuin kllp luulee, niin whitespacen järkevä käyttö on suotavaa myös koodatessa. Varsinkin kun yrittää tehdä muka-tiivistä koodia käyttämällä bittioperaattoreita ja muita lieveilmiöitä.

a1[q][w]|=1<<i;
a2[x/9]|=1<<i;
a3[x%9]|=1<<i;
t[x/9][x%9]=i;
if(ratkaise(x+1)) return 1;
a1[q][w]^=1<<i;
a2[x/9]^=1<<i;
a3[x%9]^=1<<i;

/*
 * Sama lukukelpoisesti esitettynä:
 */

a1[q][w]| = 1 << i;
a2[x/9]| = 1 << i;
a3[x%9]| = 1 << i;
t[x/9][x%9] = i;

if (ratkaise(x+1)) {
    return 1;
}

a1[q][w] ^= 1 << i;
a2[x/9] ^= 1 << i;
a3[x%9] ^= 1 << i;

kllp [10.12.2013 13:13:43]

#

Muuten ihan kiva, mutta kyseessä ei ole toimiva koodi (siis niinku tuo sun koodi).

Lisäys:

Vähän sama kuin koodaisi:

if(a= =1) a+ =1;

Kaikkea se whitespace-uskovaisuus teettääkin...

OliO [10.12.2013 20:52:06]

#

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <map>
#include <stdio.h>
#include <string.h>
using namespace std;

struct v
{
    double x,y;
    v(double a, double b):x(a),y(b){}
    v():x(0),y(0){}
};

double abs(v a)
{
    return sqrt(a.x*a.x+a.y*a.y);
}

v operator - (v a,v b)
{
    v c;
    c.x=a.x-b.x;
    c.y=a.y-b.y;
    return c;
}

double ristitulo(v o,v a, v b) //laskee vektoreiden (a-o) ja (b-O) ristitulon
{
    return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}

vector<v> pisteet;
vector<v> convex_hull;

bool pk(v a,v b)
{
    if(a.x!=b.x)
        return a.x<b.x;
    return a.y<b.y;
}

void monotoneChain()
{
    sort(pisteet.begin(),pisteet.end(),pk);
    convex_hull.resize(pisteet.size());

    int k=0;

    for(int i=0;i<pisteet.size(); i++)
    {
        while(k>=2&&ristitulo(convex_hull[k-2],convex_hull[k-1],pisteet[i])<0)
            k--;
        convex_hull[k++]=pisteet[i];
    }

    for(int i=pisteet.size()-2, t = k-1;i>=0; i--)
    {
        while(k>=t&&ristitulo(convex_hull[k-2],convex_hull[k-1],pisteet[i])<0)
            k--;
        convex_hull[k++]=pisteet[i];
    }
    convex_hull.resize(k-1);
}


int main()
{
    int n;
    cin>>n;
    for(int i=0; i<n; i++)
    {
        double a,b;
        cin>>a>>b;
        pisteet.push_back(v(a,b));
    }
    monotoneChain();
    cout<<endl;
    for(vector<v>::iterator it=convex_hull.begin();it!=convex_hull.end();it++)
        cout<<it->x<<" "<<it->y<<endl;
    return 0;
}

kukkakasvatuskilpailutehtävä^^^^^
Tälläinen coodi löytyi arkistoijeni syövereistä. Aika bloatti kyllä ;_;
Koodissahan siis muodostetaan convex hull kukkien sijainneille.

Lisäys:

Tehtävä:
Tee algoritmi joka etsii tehokkaasti pienimmän luvun a jolle pätee
a%n=n-1, kun n kuuluu [1,b] ja b on luku joka algolle annetaan.

jlaire [10.12.2013 21:10:45]

#

OliO kirjoitti:

Tehtävä:
Tee algoritmi joka etsii tehokkaasti pienimmän luvun a jolle pätee
a%n=n-1, kun n kuuluu [1,b] ja b on luku joka algolle annetaan.

ghci> let b = 100
ghci> foldl1 lcm [1 .. b] - 1
69720375229712477164533808935312303556799
ghci> all (\n -> it `mod` n == n - 1) [1 .. b]
True

Tehtävä:
Toteuta tehokkaasti seuraavat funktiot valitsemallasi kielellä.

// Kaikille
//   0 <= K <= N,
//   0 <= rank < choose(N, K)
// pätee:
//   rank_subset(N, K, unrank_subset(N, K, rank)) == rank

/**
 * 0 <= K <= N
 * subset on N kokoinen taulukko, jonka arvoista tasan K on true
 * 0 <= paluuarvo < choose(N, K)
 */
int rank_subset(int N, int K, boolean[] subset);

/**
 * 0 <= K <= N
 * 0 <= rank < choose(N, K)
 * paluuarvo on N kokoinen taulukko, jonka arvoista tasan K on true
 */
boolean[] unrank_subset(int N, int K, int rank);

choose(N, K) = N! / (K! * (N-K)!), kts. binomikerroin

Ongelma kannattaa ratkaista ensin yleisesti algoritmitasolla. Jos haluaa, voi tehdä lisäksi erityisen optimoidun koodin joillekin tietyille arvoille; esimerkiksi tapaus (N,K) = (39,7) vastaa lottorivejä, ja hyvästä ratkaisusta voi olla hyötyä Putkapostissa 45.

Hidaskin toteutus voi olla kätevä joihinkin tarkoituksiin! Pari esimerkkiä:

// Arvotaan satunnainen lottorivi
satunnainen_rivi = unrank_subset(39, 7, rand() % MAX);

// Käydään läpi kaikki eri lottorivit
for (int i = 0; i < MAX; ++i) {
    rivi = unrank_subset(39, 7, i);
    ...
}

Sisuaski [10.12.2013 23:04:15]

#

Määritetään rank_subset(n,k,subset) = monesko k:n kokoinen osajoukko subset on kun joukot järjestetään "aakkosjärjestykseen". Sääntöjen mukaisesti en ole kääntänyt joten ehkä toimii ehkä ei.

int choose[64][64]; // binomikertoimet
int rank_subset(int N, int K, const bool* S) {
  int r=0;
  for(int i=0; i<N; ++i) if (S[i]) r += choose[N-i-1][K--];
  return r;
}
void unrank_subset(int N, int K, int rank, bool* S) {
  for(int i=0; i<N; ++i)
    if (rank >= choose[N-i-1][K]) S[i] = 1, rank -= choose[N-i-1][K--];
    else S[i] = 0;
}

Tehtävä: koodaa laskin joka kykenee laskemaan lausekkeita kuten "2*16/(1+5)-5*2" ja huomioi laskujärjestyksen oikein.

Metabolix [10.12.2013 23:22:22]

#

#!/usr/bin/python3
print(eval(input("Lauseke: ")))

Oho, väärä kieli. En jaksa koodata C:llä.

Sisuaski [11.12.2013 00:31:38]

#

Ihan hauska ratkaisu, Metabolix. Aika bloatti kyllä mutta menköön tämän kerran kun ei vaadittu nopeampaa. Muista antaa uusi tehtävä seuraavalle ratkaisun yhteydessä!

Metabolix [11.12.2013 12:37:29]

#

Koodasin nyt kuitenkin myös C:llä. En jaksanut ajatella, joten käytin rekursiota. Ohjelma lopettaa lausekkeen lukemisen, kun kohtaa odottamattoman merkin, joten lausekkeet voi lopettaa esimerkiksi puolipisteeseen. Jos lausekkeen loppuessa sulut eivät täsmää, tulee virheilmoitus.

#include <stdio.h>
#include <stdlib.h>

double laske() {
	double summa();
	return summa();
}
double luku_tai_sulut() {
	double a;
	if (scanf("%lf", &a) == 1) {
		return a;
	}
	if (getchar() == '(') {
		a = laske();
		if (getchar() == ')') {
			return a;
		}
	}
	printf("Virhe lausekkeessa.\n");
	exit(1);
}
double kerto() {
	double a, b;
	char op;
	a = luku_tai_sulut();
	while (scanf(" %1[/*]", &op) == 1) {
		b = luku_tai_sulut();
		if (op == '*') {
			a *= b;
		} else {
			a /= b;
		}
	}
	return a;
}
double summa() {
	double a, b;
	char op;
	a = kerto();
	while (scanf(" %1[-+]", &op) == 1) {
		b = kerto();
		if (op == '+') {
			a += b;
		} else {
			a -= b;
		}
	}
	return a;
}

int main() {
	printf("%lg\n", laske());
	return 0;
}

Tehtävä

Antti Administraattorilla on N tietokonetta, jotka on kytketty verkoksi. Jokaisella koneella on määrätty kapasiteetti Ki, joka kertoo, montako tiedostoa koneen muistiin mahtuu. Verkossa siirrettävä tiedosto voi joka sekunti siirtyä jollekin naapurikoneista, kuitenkin niin, että minkään koneen kapasiteetti ei ylity. Siirrettäessä tiedosto häviää aiemmalta koneelta. Jos kohdekoneen kapasiteetti on jo täynnä, siirrettävä tiedosto katoaa iäksi.

Antti haluaa lähettää M tiedostoa koneelta A koneelle B. Krakkeri-Kalle yrittää estää tämän käynnistämällä verkkohyökkäyksen koneelta C: aluksi koneella C on yksi saastunut tiedosto, ja joka sekunti jokainen saastunut kone kopioi saastuneen tiedoston jokaiselle naapurilleen, jolloin Antin käytössä oleva kapasiteetti koneilla vähenee. Kallen tiedostot eivät siis poistu koneelta. Kallen tiedostot liikkuvat verkossa aina Antin tiedostojen jälkeen, eli jos Antti ja Kalle lähettävät tiedostoja samalle koneelle, Antin tiedostot tallennetaan ensin.

Laadi ohjelma, joka laskee kaikille M tiedostolle verkossa sellaiset reitit koneelta A koneelle B, että Antti saa tiedostonsa siirrettyä Kallen hyökkäyksestä huolimatta. Huomaa, että aina tällaisia reittejä ei ole!

Esimerkki: Kolme konetta on ketjuna A-C-B, ja kunkin kapasiteetti on 2. Antti siirtää yhden tiedoston koneelta A koneelle B. Aluksi koneella A on Antin tiedosto ja koneella C Kallen tiedosto. Antin tiedosto siirtyy koneelle C, jolloin koneella C on yksi Antin tiedosto ja yksi Kallen tiedosto. Kallen tiedosto taas kopioituu koneille A ja B, jolloin jokaisella koneella on yksi Kallen tiedosto. Seuraavaksi Antin tiedosto siirtyy koneelle B, jolloin Antti voittaa. (Samalla Kallen tiedosto kopioituu koneelta C koneelle A ja päinvastoin, jolloin niillä kummallakin on kaksi Kallen tiedostoa. Koneelle B Kalle ei saa toista tiedostoa, koska Antin tiedosto ehtii sinne ensin ja kone täyttyy.)

Resurssirajat voi valita kykyjen mukaan, kuitenkin mielellään N > 10 ja M > 10 ja Ki <= M.

Sisuaski [14.12.2013 15:25:47]

#

Tein nyt yksinkertaistuksella että en laske lopullisia reittejä vaan palautan vain että onnistuuko vai eikö. Reittien generoinnin voi joku viitseliäs lisätä melko helposti generoitujen taulukoiden perusteella. En myöskään jaksanut kommentoida, testata tai kääntää koodia, joten käyttö enterprice-sovelluksissa omalla vastuulla.

#include <vector>
#include <utility>
using namespace std;
vector<vector<int>> es;
vector<int> to;
vector<int> cap;
vector<bool> used;
bool dfs(int n) {
  if (!n) return 1;
  used[n]=1;
  for(int i: es[n]) if (cap[i] && !used[to[i]] && dfs(to[i])) {
    --cap[i], ++cap[i^1];
    return 1;
  }
  return 0;
}
void join(int a, int b, int c) {
  es[a].push_back(to.size());
  to.push_back(b);
  cap.push_back(c);
  es[b].push_back(to.size());
  to.push_back(a);
  cap.push_back(0);
}
// syöte
int N, M;
vector<int> K;
vector<vector<int>> E;
int A,B,C;

bool solve() {
  vector<int> ss(N);
  ss[C]=1;
  es.resize(1+N);
  while(K[B]-ss[C]>=M) {
    int k = es.size();
    es.resize(k+2*N);
    for(int i=0; i<N; ++i) if (K[i]>ss[i]) {
      join(k-N+i,k+i,M);
      join(k+i,k+i+N,K[i]-ss[i]);
      for(int j: E[i]) join(k-N+i,k+j,M);
    }
    join(k+B+N,0,M);
    while(used.clear(),used.resize(es.size()),dfs(1+A))
      if (!--M) return 1;
    for(int i=0; i<N; ++i) if (ss[i]) for(int j: E[i]) ++ss[j];
  }
  return 0;
}

Tehtävä: Toteuta Huffmanin koodaus. Itse pakkausta/purkua ei tarvitse toteuttaa, vaan riittää määrittää kaikille annetussa tiedostossa esiintyville merkeille pakkauksessä käytettävät bittijonot.

kllp [15.12.2013 16:01:32]

#

#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
struct S {
    int F,A,L,R;
};
vector<S> v;
bool c(int a, int b) {
    return v[a].F>v[b].F;
}
vector< vector<bool> > lol(1000);
void e(int k, vector<bool> & a) {
    if(v[k].L==-1 && v[k].R==-1) {
        lol[v[k].A]=a;
        return;
    }
    a.push_back(0);
    e(v[k].L, a);
    a[a.size()-1]=1;
    e(v[k].R, a);
    a.pop_back();
}
int main() {
    vector<int> h;
    string s;
    getline(cin, s);
    map<char, int> f;
    for(int i=0; i<s.size(); ++i) {
        ++f[s[i]];
    }
    S q;
    q.L=-1;
    q.R=-1;
    for(map<char, int>::iterator it=f.begin(); it!=f.end(); ++it) {
        q.F=it->second;
        q.A=it->first;
        h.push_back(v.size());
        v.push_back(q);
    }
    make_heap(h.begin(), h.end(), c);
    for(;h.size()>1;) {
        int a,b;
        a=h[0];
        pop_heap(h.begin(), h.end(), c);
        h.pop_back();
        b=h[0];
        pop_heap(h.begin(), h.end(), c);
        h.pop_back();
        q.F=v[a].F+v[b].F;
        q.L=a;
        q.R=b;
        h.push_back(v.size());
        v.push_back(q);
        push_heap(h.begin(), h.end(), c);
    }
    vector<bool> a;
    e(h[0], a);
    for(int i=0; i<lol.size(); ++i) {
        if(lol[i].size()) {
            cout<<char(i)<<' ';
            for(int j=0; j<lol[i].size(); ++j) {
                cout<<lol[i][j];
            }
            cout<<'\n';
        }

    }

}

Ehkä toimii, ehkä ei ;_; Käänsinkin ja testailin, mutta en silti tiedä Q_Q
En uskalla vielä edes laittaa uutta tehtävää T_T
En kyllä edes tietäisi vielä mitään tehtävää QAQ

kllp [15.12.2013 20:50:31]

#

Tehtävä: Tee ohjelma, jolla voi ratkaista yhtälöitä muotoa ax + by = c, jossa a,b,c,x,y kokonaislukuja. Syötteenä ohjelmalle annetaan kolme lukua: a, b ja c. Ohjelman tulisi etsiä kaikki lukuparit (x,y), joilla yhtälö toteutuu tai ilmoittaa, että ratkaisua ei ole. Voit itse päättää tarkemmin ratkaisun muotoilun.

Deffi [15.12.2013 22:14:24]

#

#include <stdio.h>
#include <stdlib.h>

long gcd(long a, long b) {
    return (b == 0) ? a : gcd(b, a % b);
}

void eea(long a, long b, long c, long *x, long *y) {
    long d[2][3];
    int eka, toka;

    d[0][0] = 1; d[0][1] = 0; d[0][2] = a;
    d[1][0] = 0; d[1][1] = 1; d[1][2] = b;

    eka = 0; toka = 1;
    while (d[toka][2] != c) {
        long q = d[eka][2] / d[toka][2];
        d[eka][0] = d[eka][0] - q * d[toka][0];
        d[eka][1] = d[eka][1] - q * d[toka][1];
        d[eka][2] = d[eka][2] - q * d[toka][2];
        eka ^= 1; toka ^= 1;
    }
    *x = d[toka][0]; *y = d[toka][1];
}

int main(int argc, char *argv[]) {
    long a, b, c, x, y;
    a = atoi(argv[1]); b = atoi(argv[2]); c = atoi(argv[3]);
    if (a < b) { a ^= b; b ^= a; a ^= b; }
    if (c % gcd(a, b) == 0) {
        eea(a, b, gcd(a, b), &x, &y);
        x *= c / gcd(a, b);
        y *= c / gcd(a, b);

        printf("%ldx + %ldy = %ld\n", a, b, c);
        printf("x = %ld + %ldt\n", x, b / gcd(a, b));
        printf("y = %ld - %ldt\n", y, a / gcd(a, b));
    } else printf("ei ratkee\n");
    return 0;
}

Deffi [16.12.2013 15:04:49]

#

Ei sovi kovin hyvin "koodaa kääntämättä" formaattiin, mutta menkööt.

Tehtävä
Uolevi suojaa tietokoneohjelmansa salasanalla. Salasana on 32-bittinen kokonaisluku, joka annetaan syötteeksi funktiolle f. Salasanan tulkitaan olevan oikein, jos funktio kuvaa salasanan luvuksi 0x1337C0DE. Tehtävänäsi on selvittää Uolevin salasana, kun funktiolla tiedetään olevan seuraavat ominaisuudet (0 ≤ x < 232, 0 ≤ y < 232):

Tai toisin sanoen selkokielellä: jokainen ykkösbitti syötteessä kääntää tietyn joukon bittejä tuloksessa. Nämä bittijoukot on annettu alla olevan koodin A-taulukossa. Selvitä, mitkä syötteen bitit pitää asettaa ykkösiksi, että funktion tulokseksi tulee 0x1337C0DE.

#include <stdio.h>
#include <stdint.h>

uint32_t A[32] = {
        0xB8BC6765, 0xAA09C88B, 0x8F629757, 0xC5B428EF, 0x5019579F,
        0xA032AF3E, 0x9B14583D, 0xED59B63B, 0x01C26A37, 0x0384D46E,
        0x0709A8DC, 0x0E1351B8, 0x1C26A370, 0x384D46E0, 0x709A8DC0,
        0xE1351B80, 0x191B3141, 0x32366282, 0x646CC504, 0xC8D98A08,
        0x4AC21251, 0x958424A2, 0xF0794F05, 0x3B83984B, 0x77073096,
        0xEE0E612C, 0x076DC419, 0x0EDB8832, 0x1DB71064, 0x3B6E20C8,
        0x76DC4190, 0xEDB88320
};

uint32_t f(uint32_t x) {
    uint32_t y, i;
    y = 0;
    for (i = 0; i < 32; i++) {
        if (x & 1)
            y ^= A[i];
        x >>= 1;
    }
    return y;
}

int main(int argc, char *argv[]) {
    uint32_t salasana;
    printf("Anna salasana: 0x"); fflush(stdout);
    scanf("%x", &salasana);
    if (f(salasana) == 0x1337C0DE) {
        printf("oikein!\n");
    } else printf("väärin!\n");
    return 0;
}

Algoritmin olisi toivottavaa toimia polynomisessa ajassa salasanan pituuden suhteen. Bruteforce on häpeällinen ratkaisu.

Antti Laaksonen [16.12.2013 20:42:34]

#

Seuraava funktio g on funktio f:n käänteisfunktio:

uint32_t B[32];
uint32_t C[32];

uint32_t g(uint32_t s) {
    for (int i = 0; i < 32; i++) {
        for (int j = 0; j < 32; j++) {
            B[i] ^= ((A[j]>>i)&1)<<j;
        }
        C[i] = (s>>i)&1;
    }
    for (uint32_t b = 0x80000000; b >= 1; b >>= 1) {
        int q;
        for (int i = 0; i < 32; i++) {
            if ((B[i]&b) && (B[i]^b)<b) q = i;
        }
        for (int i = 0; i < 32; i++) {
            if (!(B[i]&b) || i == q) continue;
            B[i] ^= B[q];
            C[i] ^= C[q];
        }
    }
    uint32_t t = 0;
    for (uint32_t i = 0; i < 32; i++) {
        if (C[i]) t ^= B[i];
    }
    return t;
}

Tehtävä
Sinulle annetaan merkkijono, joka muodostuu n (1 ≤ n ≤ 5000) merkistä A..Z, sekä kokonaisluku k (0 ≤ kn-2). Saat muokata merkkijonoa rajattomasti operaatiolla, jossa kaksi merkkiä, joiden välissä on k merkkiä, vaihdetaan keskenään. Montako erilaista merkkijonoa voit muodostaa tällä tavalla? Ilmoita vastaus modulo 1000000007.

Esimerkiksi jos merkkijono on ABBC ja k = 1, niin voit muodostaa 4 merkkijonoa: ABBC, ACBB, BBAC ja BCAB.

OliO [16.12.2013 21:14:22]

#

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
#define K_MAX 100000
int muisti[256][K_MAX+1];

int main()
{
    string s;
    int k;
    cin>>s>>k;
    int n=s.size();
    for(int i=0;i<n;i++)
    {
        muisti[s[i]][i%(k+1)]++;
    }

    long long vastaus=1;

    for(int i=0;i<k+1;i++)
    {
        int ker=1;
        long long osavastaus_ala=1;
        for(int e='A'; e<='Z';e++)
        {
            int q=1;
            long long osavastaus_yla=1;
            while(muisti[e][i])
            {
                osavastaus_yla*=ker;
                osavastaus_ala*=q;

                long long a=__gcd(osavastaus_yla,osavastaus_ala);

                osavastaus_yla/=a;
                osavastaus_ala/=a;

                osavastaus_yla%=1000000007;
                q++;ker++;
                muisti[e][i]--;
            }
            vastaus*=osavastaus_yla;
            vastaus%=1000000007;
        }
    }
    cout<<vastaus<<endl;;
    return 0;
}

Ja tämähän toimii niin että muodostu k+1 ryhmää jotka eivät voi vaihtaa kirjaimia keskenään. Sitten vaan lasketaan erilaiset permutaatiot.

EDIT: muokkasin koodini toimimaan paremmin(/huonommin)

kllp [16.12.2013 21:33:35]

#

#include <iostream>
using namespace std;
typedef long long ll;
#define MOD 1000000007
ll exp(ll a, ll b) {
    ll t=1;
    for(;b;b/=2) {
        if(b&1) {
            t*=a;
            t%=MOD;
        }
        a*=a;
        a%=MOD;
    }
    return t;
}
int main() {
    int k;
    cin>>k;
    string a;
    cin>>a;

    ll s=1;
    for(int i=0; i<a.size(); ++i) {
        int m['Z']={0};
        ll q=0;
        for(int j=i; a[j]!=0 && j<a.size(); j+=k+1) {
            ++m[a[j]-'A'];
            ++q;
            a[j]=0;
        }
        ll w=1;
        for(int i=1; i<=q; ++i) {
            w*=i;
            w%=MOD;
        }
        ll e=1;
        for(int i=0; i<'Z'; ++i) {
            for(ll j=1; j<=m[i]; ++j) {
                e*=j,e%=MOD;
            }
        }
        s*=(w*exp(e, MOD-2))%MOD;
        s%=MOD;
    }
    cout<<s<<'\n';
}

Tuossa minunkin ratkaisuni. Emt toimiiko.

OliO [16.12.2013 21:54:41]

#

Tehtävä: Petteri Peelo pitää neliöistä ja neliöiden rakentamisesta. Eräänä päivänä Petterille tuli kuitenkin ongelma: Hänelle oli jäänyt vain yhdenlaisia rakennuspalikoita jäljelle. Koska petteri rakastaa kaikenkokoisia neliöitä, tulee hän kaikkein onnellisimmaksi kun hän saa rakennettua mahdollisimman monta neliötä.

Tehtävänäsi on etsiä pienin neliö joka voidaan rakentaa annetunmuotoisista paloista. Syötteenä saat palikan muodot vapaavalintaisessa muodossa, esimerkiksi:

100
100
100
110

tai

(0,0),(0,1),(0,2),(0,3),(1,3).


Tarkennus tehtävään: Tarkoituksena ei ole etsiä kovinkaan tehokasta menetelmää pienimmän neliön saavuttamiseksi, vaan vain yleinen menetelmä joka onnistuu siinä(mielellään hieman nopeampi kuin brute kuitenkin). Itqpotqraivarien takia muutan tehtävää niin ettei tarvitse etsiä kuin neliö joka on pienempi kuin 40(<-- sivun pituus). Jos ei ole, tulosta -1.

Deffi [18.12.2013 13:31:50]

#

Ei ole nyt C-kääntäjää käsillä, joten käytän aacoodia:

Va,ka: palikat, kalikat.
Goto: pelle peloton: talo.
Ot-yhteys: pelle peloton.
Hkr: robotti.
Send: cmd: rakenna, tyyppi:neliö.
Va,ka: neliöt.
Send-tänne.

Edit: oli syntaxi virhe (nyt korjattu)

Tehtävä
OliOn tehtävä uudelleen

Sisuaski [18.12.2013 22:02:33]

#

Vähän clean codea suomeksi:

#include <vector>
#include <algorithm>
using namespace std;

/**
 * Laskee annetuista lukujoukoista eksaktin peitteen.
 * Funktio saa parametrinaan luvun N ja joukon rivejä.
 * Jokainen rivi sisältää kokonaislukuja väliltä 0..N-1,
 * ja funktio valitsee sellaisen rivien osajoukon että jokainen
 * luku 0..N-1 kuuluu tasan yhteen valittuun riviin.
 * Funktio palauttaa valittujen rivien indeksit rivit-taulukossa,
 * tai tyhjän listan jos ratkaisua ei ole olemassa.
 */
vector<int> eksaktiPeite(int N, vector<vector<int>> rivit);

typedef pair<int,int> P;
typedef vector<P> V;
V siirra(V v, int x, int y) {
  for(P& p: v) p.first+=x, p.second+=y;
  return v;
}
V normalisoi(V v) {
  int x=1e9, y=1e9;
  for(P p: v) x = min(x, p.first), y = min(y, p.second);
  return siirra(v, -x, -y);
}

vector<V> rakennaNelio(V pala) {
  vector<V> perusPalat;
  for(int i=0; i<2; ++i) {
    for(int j=0; j<4; ++j) {
      perusPalat.push_back(normalisoi(pala));
      for(P& p: pala) p = P(p.second, -p.first);
    }
    for(P& p: pala) p = P(-p.first, -p.second);
  }
  sort(perusPalat.begin(), perusPalat.end());
  perusPalat.erase(unique(perusPalat.begin() perusPalat.end()), perusPalat.end());
  for(int s=1; s<40; ++s) {
    vector<V> palat;
    for(V v: perusPalat) {
      int x=0,y=0;
      for(P p: v) x=max(x,p.first), y=max(y,p.second);
      for(int i=0; x+i<s; ++i) for(int j=0; y+j<s; ++j)
        palat.push_back(siirra(v,i,j));
    }
    vector<vector<int>> rivit;
    for(V& v: palat) {
      vector<int> u;
      for(P p: v) u.push_back(s*p.first + p.second);
      rivit.push_back(u);
    }
    vector<int> peite = eksaktiPeite(s*s, rivit);
    if (!peite.empty()) {
      vector<V> tulos;
      for(int i: peite) tulos.push_back(palat[i]);
      return tulos;
    }
  }
  return {};
}

Tehtävä: Toteuta edellisen koodin alussa kuvattu eksaktiPeite-funktio haluamallasi ohjelmointikielellä (muokaten funktion parametrien ja paluuarvon tyyppejä tarpeen mukaan). Voit käyttää esim. Knuthin Dancing Links -algoritmia (tai sitten perinteikästä raakaa voimaa jos tuo tuntuu liian monimutkaiselta).

kllp [23.12.2013 02:32:22]

#

Tässä kamala sekoitus raakaa voimaa ja Dancing Links -räpellystä. Hyi olkoon. En ole aivan varma toimiiko. N-queen ongelmasta antoi oikeita vastauksia, mutta sen jälkeen jouduin tekemään muutoksia koodiin, että kelpaisi tähän vastauksksi.

#include <iostream>
#include <vector>
#define NN 1000
#define MM 1000
using namespace std;
struct S {
    int L,R,U,D;
};
S v[NN][MM];
int yk[MM];
int sarakkeita=0;

/*
 * palautettavaVectori sisältää eksaktiPeitteen palauttaman vectorin
*/
vector<int> palautettavaVectori;
int e(int x, int y, int d, int N) {

    palautettavaVectori.push_back(x);
    int lol=0;
    vector<pair<int, int> > pois;
    for(int q = y; q != y || !lol; q = v[x][q].R) {
        lol=1;
        int lol2=0;
        int maara=0;
        int raja=yk[q];
        for(int w = x; maara<=raja; w = v[w][q].U, ++maara) {

            lol2 = 1;
            if(w == 0) {
                int left = v[w][q].L;
                int right = v[w][q].R;
                v[w][left].R = right;
                v[w][right].L = left;
                --sarakkeita;
            }

            int lol3 = 0;
            for(int e = q; e != q || !lol3; e = v[w][e].R) {
                lol3 = 1;
                pois.push_back(make_pair(w, e));
                if(w!=0) --yk[e];
                int up = v[w][e].U;
                int down = v[w][e].D;
                v[up][e].D = down;
                v[down][e].U = up;
                if(w == 0 || w == x) break;
            }
            if(v[w][q].U==w)  {
                break;
            }
        }
    }
    if(!sarakkeita) return 1;
    int pk=-1;
    int pm=1e9;
    //tämä on nolo... anteeksi...
    for(int i = 0; i<N; i=v[0][i].R) {
        if(yk[i] && yk[i] < pm) {
            pm = yk[i];
            pk = i;
        }
        if(v[0][i].R<i) break;
    }
    int ma=0;
    if(pk != -1) {
        for(int i = v[0][pk].U; i != 0; i = v[i][pk].U) {
            if(e(i, pk, d+1, N)) return 1;
        }
    }
    lol = 0;
    for(int q = y; q != y || !lol; q = v[x][q].R) {

        lol=1;
        int lol2=0;
        int maara=0;
        for(int w = x; (w != x || !lol2); w = v[w][q].U) {
            lol2 = 1;
            if(w == 0) {
                int left = v[w][q].L;
                int right = v[w][q].R;
                v[w][left].R = q;
                v[w][right].L = q;
                ++sarakkeita;
            }
            int lol3 = 0;
            for(int e = q; e != q || !lol3; e = v[w][e].R) {
                lol3 = 1;
                if(w!=0) ++yk[e];
                int up = v[w][e].U;
                int down = v[w][e].D;
                v[up][e].D = w;
                v[down][e].U = w;
                if(w== 0 || w == x) break;
            }
        }
    }
    palautettavaVectori.pop_back();
    return ma;
}
bool t[NN][MM];
vector<int> eksaktiPeite(int N, vector< vector<int> > rivit) {
    //rivienMaara-muuttuja sisaltaa rivien lukumaaran
    int rivienMaara = rivit.size();
    sarakkeita=N;
    for(int i = 0; i < rivienMaara; ++i) {
        for(int j = 0; j < rivit[i].size(); ++j) {
            t[i][rivit[i][j]]=1;
        }
    }
    for(int i=0; i<N; ++i) {
        v[0][i].R=(i+1)%(N);
        v[0][i].L=(i+N-1)%(N);
    }
    int edu=0;
    int edr=-1;

    for(int i=0; i<N; ++i) {
        edu=0;
        for(int j=1; j<=rivienMaara; ++j) {
            if(t[j-1][i]) {
                ++yk[i];
                v[edu][i].D=j;
                v[j][i].U=edu;
                edu=j;
            }
        }
        v[0][i].U=edu;
        v[edu][i].D=0;
    }
    for(int i = 1; i <= rivienMaara; ++i) {
        int edl = 0;
        int eka = -1;
        int lol=0;

        for(int j = 0; j%N != eka; ++j, ++lol) {
            int jj=j%N;
            if(eka!=-1 && t[i-1][jj]) {
                v[i][jj].L=edl;
                v[i][edl].R=j;
            }
            if(t[i-1][jj]) {
                if(eka == -1) eka = jj;
                edl = jj;
            }
            if(lol > N && eka == -1) {
                break;
            }
        }
        if(eka != -1) {
            v[i][edl].R = eka;
            v[i][eka].L = edl;
        }
    }
    int ma=0;
    for(int i=1; i<=rivienMaara; ++i) {
        for(int j=0; j<N; ++j) {

            if(t[i-1][j]) {
                if(e(i, j, 0, N)) {
                    return palautettavaVectori;
                }
            }
        }
    }
    cout<<ma<<'\n';
    return palautettavaVectori;
}

Grez [23.12.2013 08:38:57]

#

Itse väkersin tuohon eksaktipeitteeseen pari päivää sitten tällaisen yksinkertaisen ja varmaankin isommilla joukoilla melko epätehokkaan lähestymistavan, mutta en jaksanut laittaa tuolloin kun en keksinyt mitään kivaa uutta tehtävää:

using System;
using System.Collections.Generic;
using System.Linq;

class ep {

        public static List<int> eksaktiPeite(int N, List<List<int>> rivit)
        {
            return eksaktiPeite(N, rivit, new HashSet<int>(), null, 0);
        }
        private static List<int> eksaktiPeite(int N, List<List<int>> rivit, HashSet<int> käytetytRivit, List<int> löydetyt, int seuraavaRiviIx)
        {
            if (löydetyt != null && rivit[seuraavaRiviIx].Intersect(löydetyt).Any()) { return null; }
            var uusiLöydetyt = (löydetyt == null)
                ? new List<int>()
                : löydetyt.Concat(rivit[seuraavaRiviIx]).ToList();

            if (uusiLöydetyt.Count == N) { return käytetytRivit.ToList(); }
            for (int riviIx = 0; riviIx < rivit.Count; riviIx++)
            {
                if (!käytetytRivit.Contains(riviIx))
                {
                    käytetytRivit.Add(riviIx);
                    var tulos = eksaktiPeite(N, rivit, käytetytRivit, uusiLöydetyt, riviIx);
                    if (tulos != null) { return tulos; }
                    käytetytRivit.Remove(riviIx);
                }
            }
            return null;
        }
}

kllp [24.12.2013 03:18:38]

#

Toivottavasti nyt ei ole jo vanhan toistoa, mutta aihe on vain niin I-H-A-N-A!

Tehtävä: Uolevin kukkienkasvatus etenee varsin hienosti. Kukkia pitää tietysti muistaa myös kastella, ja Uolevilla onkin siihen hieno kastelukannu. Uolevin kastelukannu on siitä erikoinen, että sillä voi valita kukkamaalta jonkin suorakulmion muotoisen alueen ja kastella sitä tietyllä määrällä vettä.

Uolevin ystävä, professori Viherkasvi neuvoo Uolevia kastelemaan kukkia järjestelmällisesti niin, että kukat saisivat kaikki yhtä paljon vettä. Uolevia ei kuitenkaan moinen kiinnosta: hän kastelee sellaisia alueita kuin häntä huvittaa.

Tästä seuraa kuitenkin ongelmia. Uolevin kasvimaa on kohtuullisen suuri: 10^9 x 10^9 kasvijuttuyksikköä. Uolevilla onkin nyt vaikeuksia laskea tehokkaasti, paljonko eri alueet ovat saaneet vettä. Kirjoita Uoleville ohjelma, joka ratkaisee ongelman.

Syöte:
Ensimmäisellä rivillä kokonaisluku q, joka kertoo kyselyiden määrän.
Kyselyitä on kahta tyyppiä (tyyppien järjestys voi olla mikä tahansa):

Tyyppi yksi (rivi alkaa kirjaimelle A): Uolevi kastelee alueen jollakin määrällä vettä. Alueen kaksi kärkipistettä ilmoitetaan neljällä kokonaisluvulla x1, y1, x2, y2. Alueen jokaisen kukkayksikön saaman veden määrän ilmoittaa kokonaisluku v. Tässä ohjelma ei tulosta mitään.

Tyyppi kaksi (rivi alkaa kirjaimella B): Uolevi haluaa tietää, paljonko tietty alue on saanut vettä. Alueen kaksi kärkipistettä ilmoitetaan neljällä kokonaisluvulla x1, y1, x2, y2. Tässä kohtaa ohjelman tulisi siis tulostaa alueen yhteensä saama vesimäärä.

Ohjelma voi lukea ensin kaikki syötteet ja tulostaa vasta sitten.

Voit olettaa, että kyselyitä tyyppiä yksi on noin 100 000 ja kyselyitä tyyppiä kaksi on noin 100 000. Ohjelman tulisi toimia pahimmassakin tapauksessa muutamassa sekunnissa.

Selvennyksenä vielä: kaikki ohjelman käsittelemät "alueet" ovat suorakulmion muotoisia.

Vinkki: 1. 2d-segmenttipuu 2. Emt mikä se termi oikeasti on, mutta jos jotenkin "harvasti" toteuttaisi 3. En tiedä suomenkielistä termiä, mutta ehkä lazy segment tree auttaa

EDIT: Nyt on ehkä vähän selkeämpi?

EDIT2: Juuh elikkäs vaihdoin vielä vähän noita syötteen ylärajoja... Jos haluatte olla varmoja, niin suosittelen odottamaan huomiseen. :Dd

EDIT3:
Tehtävän haastavuuden vuoksi riittää ratkaisu tapauksessa, jossa tyypin 1 kyselyissä x1 = x2 ja y1 = y2 eli alueen pinta-ala on 1

Grez [24.12.2013 03:32:01]

#

Edit: Voidaan varmaan olettaa että kaikki rivit A tulee ennen ekaa riviä B.

Edit 2: Okei, kirjoitit että syötteet voi tulla missä järjestyksessä vaan (siis B voi tulla ennen A:ta) ja että ohjelma _voi_ lukea kaikki syötteet ja tulostaa vasta sitten. Nyt näkisin että ohjelma antaa eri tulokset jos tulostellaan sitä mukaa kun B:tä tulee vs. jos luetaan ensin kaikki (A:t) sisään.

Ennen editointia olleet kysymykset, joihin tuli jo tarkennus:

(Eli mitä algoritmi saa syötteenä ja palauttaa, tarkalleen ottaen?

Onko tässä jokin suurempi idea kuin pyöräyttää läpi suorakulmioiden leikkauksia ja niiden summia?

Eikö tyypissä A pitäisi myös kertoa mikä se jokin vesimäärä on (per kukka esimerkiksi?))

kllp [24.12.2013 19:27:37]

#

Ei pitäisi riippua ohjelman tulostus tuosta. Lähinnä ajattelin mainita koordinaattien pakkaamisen vuoksi, jos joku haluaisi siten tuota tehdä.

Sisuaski [29.12.2013 13:48:21]

#

Tässä on moniulotteinen ratkaisu siltä varalta että Uolevi siirtyy 3- tai vaikka 4-ulotteisten peltojen viljelyyn. Ei ole testattu muuta kuin 1- ja 2-ulotteisissa tapauksissa mutta pitäisi toimia aina induktion nojalla.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;

#ifndef DIM
#define DIM 2
#endif

vector<int> xs[DIM];

int roundTo2(int n) {
	while(n&(n-1)) n+=n&-n;
	return n;
}
template<class T, int D>
struct SegTree {
	SegTree<T,D> *children=0;
	T value;
	template<class F>
	void doRange(int a, int b, int from, int to, F f) {
		int xa=xs[D-1][a], xb=xs[D-1][b];
		if (xb <= from) return;
		if (xa >= to) return;
		if (xa>=from && xb<=to) {
			f.in(value, xa, xb);
		} else {
			f.border(value, max(xa, from), min(xb, to));
			int m = (a+b)/2;
			if (!children) children = new SegTree[2];
			children[0].doRange(a, m, from, to, f);
			children[1].doRange(m, b, from, to, f);
		}
	}
	template<class F>
	void doRange(int from, int to, F f) {
		doRange(0, xs[D-1].size()-1, from, to, f);
	}
};
template<int D>
struct Box {
	int x1,x2;
	Box<D-1> rest;

	void read(bool second) {
		int& x = second?x2:x1;
		cin>>x;
		xs[D-1].push_back(x);
		rest.read(second);
	}
};
template<> struct Box<0> {
	void read(bool) {}
};

template<int D> struct SumNode;

template<int D>
struct AddSum {
	Box<D-1> box;
	ll amount;
	void in(SumNode<D>& n, int a, int b) {
		n.sum.add(box, amount);
	}
	void border(SumNode<D>& n, int a, int b) {
		n.parentSum.add(box, amount * (b-a));
	}
};
template<int D>
struct GetSum {
	Box<D-1> box;
	ll& res;
	void in(SumNode<D>& n, int a, int b) {
		res += n.parentSum.get(box);
		border(n,a,b);
	}
	void border(SumNode<D>& n, int a, int b) {
		res += n.sum.get(box) * (b-a);
	}
};

template<int D>
struct SumTree {
	SegTree<SumNode<D>,D> tree;

	void add(Box<D> box, ll amount) {
		tree.doRange(box.x1, box.x2, AddSum<D>{box.rest, amount});
	}
	ll get(Box<D> box) {
		ll res=0;
		tree.doRange(box.x1, box.x2, GetSum<D>{box.rest, res});
		return res;
	}
};
template<>
struct SumTree<0> {
	ll sum=0;
	void add(Box<0>, ll amount) {
		sum += amount;
	}
	ll get(Box<0>) {
		return sum;
	}
};

template<int D>
struct SumNode {
	SumTree<D-1> sum, parentSum;
};

struct Q {
	char c;
	Box<DIM> box;
	int v;
};
int main() {
	int q;
	cin>>q;
	vector<Q> qs(q);
	for(Q& q: qs) {
		cin>>q.c;
		q.box.read(0);
		q.box.read(1);
		if (q.c=='A') cin>>q.v;
	}
	for(auto& v: xs) {
		sort(v.begin(), v.end());
		v.erase(unique(v.begin(), v.end()), v.end());
	}
	SumTree<DIM> tree;
	for(Q q: qs) {
		if (q.c=='A') tree.add(q.box, q.v);
		else cout<<tree.get(q.box)<<'\n';
	}
}

Sisuaski [29.12.2013 20:08:13]

#

Tehtävä:
On annettu korkeintaan 50 lukua väliltä [-1017, 1017], ja luku S väliltä [-5*1018, 5*1018].
Valitse annetuista luvuista osajoukko, jonka summa on mahdollisimman suuri mutta korkeintaan S. Ohjelman pitäisi tuottaa ratkaisu 50 luvulla korkeintaan minuutissa.

Metabolix [29.12.2013 22:45:38]

#

Jaetaan joukko puoliksi, lasketaan puoliskojen kaikkien osajoukkojen summat, lajitellaan, valitaan paras osajoukkopari.

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>

// Osajoukon summa ja bittimaski.
struct sum {
	long value, mask;
	bool operator < (const sum& t) const {
		return value < t.value;
	}
	sum operator + (const sum& t) const {
		return sum {value + t.value, mask | t.mask};
	}
};

int main() {
	long n, s;
	std::vector<long> values;

	// Luetaan arvot.
	std::cin >> n >> s;
	for (int i = 0; i < n; ++i) {
		long v;
		std::cin >> v;
		values.push_back(v);
	}

	// Jaetaan joukko puoliksi ja lasketaan molemmista kaikki summat.
	long n1 = n / 2, n2 = n - n1;
	std::vector<sum> sums[2];
	sums[0].resize(1 << n1);
	sums[1].resize(1 << n2);
	for (int i = 0; i < n1; ++i) {
		for (int j = 0; j < 1L << i; ++j) {
			sums[0][(1L << i) + j] = sums[0][j] + sum {values[i], 1L << i};
		}
	}
	for (int i = 0; i < n2; ++i) {
		for (int j = 0; j < 1L << i; ++j) {
			sums[1][(1L << i) + j] = sums[1][j] + sum {values[n1 + i], 1L << (n1 + i)};
		}
	}

	// Laitetaan toiset summat nousevaan ja toiset laskevaan järjestykseen.
	std::sort(sums[0].begin(), sums[0].end());
	std::sort(sums[1].rbegin(), sums[1].rend());

	// Etsitään paras summapari.
	sum best {std::numeric_limits<long>::min()};
	for (auto a = sums[0].begin(), b = sums[1].begin(); a != sums[0].end() && b != sums[1].end();) {
		if (a->value + b->value <= s) {
			if (a->value + b->value > best.value) {
				best = *a + *b;
			}
			++a;
		} else {
			++b;
		}
	}

	// Tulostetaan vastaus: paras summa = arvo1 + ... + arvoN.
	std::cout << best.value;
	const char* str = " = ";
	for (int i = 0; i < n; ++i) if (best.mask & (1L << i)) {
		std::cout << str << values[i];
		str = " + ";
	}
	std::cout << std::endl;
}

Tehtävä

Lauseke voi sisältää laskutoimituksia +, -, * ja /, sulkuja ja yksikirjaimisia muuttujia a-z, jotka ovat positiivisia reaalilukuja. Montako merkitykseltään erilaista enintään N merkin mittaista lauseketta näistä voidaan muodostaa? (Esimerkiksi lausekkeet a ja b ovat merkitykseltään erilaiset mutta a ja b*a/b sekä a*(b/b)/c*c ovat supistettuna sama lauseke.)


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta