#include #include #include #include struct znak { //znak reprezentovaný polem řádků, pro každý řádek je hodnotou číslo vybrveného sloupce unsigned * radky; }; struct bitove_pole { uint8_t * bity; unsigned pozice1, pozice2; //aktuální pozice při průchodu, pozice1 určuje byte, pozice2 bit v bytu unsigned obsazeno1, obsazeno2; //počet plně obsazených bytů, obsazených bitů v posledním bytu unsigned alokovano; //alokovaných bytů }; unsigned N, K; struct bitove_pole prectene; //seznam přečtených znaků unsigned log_2(unsigned x) { //spočítá ceil(log_2(x)) x = x - 1; unsigned result = 0; while (x > 0) { result++; x >>= 1; } return result; } //vrátí číslo zadaného znaku; pokud je zadaný znak větší než největší platný znak, vrátí počet možných znaků //prochází postupně permutace řádků a porovnává se zadaným znakem unsigned cislo_znaku(struct znak z) { struct znak generovany; generovany.radky = malloc(sizeof(&generovany.radky) * N); bool * sloupce = calloc(N, sizeof(bool)); //sloupce a diagonály zakázané v aktuálním řádku bool * diag1 = calloc(2 * N - 1, sizeof(bool)); bool * diag2 = calloc(2 * N - 1, sizeof(bool)); generovany.radky[0] = 0; unsigned shodnych_radku = 0; unsigned nalezenych_znaku = 0; unsigned i = 0; bool konec = false; while (!konec) { unsigned j = generovany.radky[i]; if (j >= N) { //prošli jsme celý řádek if (i == 0) konec = true; //prošli jsme všechny znaky else { //vracíme se o řádek výš i--; j = generovany.radky[i]; sloupce[j] = false; diag1[i+j] = false; diag2[N+i-j] = false; generovany.radky[i]++; } } else if (!sloupce[j] && !diag1[i+j] && !diag2[N+i-j]) { //jde umístit pixel if (z.radky[i] == j && i == shodnych_radku) shodnych_radku++; if (i == N - 1) { //poslední řádek if (shodnych_radku == N) konec = true; //našli jsme shodný znak else { //našli jsme další (neshodný) znak, vracíme se o řádek výš nalezenych_znaku++; i--; j = generovany.radky[i]; sloupce[j] = false; diag1[i+j] = false; diag2[N+i-j] = false; generovany.radky[i]++; } } else { //jdeme o řádek níž sloupce[j] = true; diag1[i+j] = true; diag2[N+i-j] = true; i++; generovany.radky[i] = 0; } } else generovany.radky[i]++; //zkusíme další sloupec v řádku } free(generovany.radky); free(sloupce); free(diag1); free(diag2); return nalezenych_znaku; } void nacti_znak(struct znak * z) { //do z uloží znak načtený ze vstupu unsigned x; for (unsigned i = 0; i < N; i++) for (unsigned j = 0; j < N; j++) { scanf("%u", &x); if (x == 1) z->radky[i] = j; } } //určuje, jestli ještě jsou v bitovém poli p nějaké nepřečtené bity bool jde_cist_bity(struct bitove_pole * p) { return (p->pozice1 < p->obsazeno1) || ((p->pozice1 == p->obsazeno1) && (p->pozice2 < p->obsazeno2)); } unsigned cti_bity(struct bitove_pole * p, unsigned n) { //přečte n bitů z bitového pole unsigned result = 0; unsigned precteno = 0; while (precteno < n) { if (n - precteno >= 8 - p->pozice2) //ctu do konce bytu { result |= (p->bity[p->pozice1] >> p->pozice2) << precteno; precteno += 8 - p->pozice2; p->pozice1++; p->pozice2 = 0; } else { result |= ((p->bity[p->pozice1] >> p->pozice2) & ((1 << (n-precteno)) - 1)) << precteno; p->pozice2 += n - precteno; precteno += n - precteno; } } return result; } //přečte z bitového pole číslo z {a, ..., b} unsigned cti_z_intervalu(struct bitove_pole * p, unsigned a, unsigned b) { return a + cti_bity(p, log_2(b-a+1)); } //zapíše na konec bitového pole n bitů uložených v parametru x void zapis_bity(struct bitove_pole * p, unsigned x, unsigned n) { if ((p->alokovano - p->obsazeno1) * 8 - p->obsazeno2 < n) { //musíme zvětšit pole p->alokovano = p->alokovano + (n - ((p->alokovano - p->obsazeno1) * 8 - p->obsazeno2) + 7) / 8; //+7 kvůli zaokrouhlení nahoru p->bity = (uint8_t *)realloc(p->bity, p->alokovano); } unsigned zapsano = 0; while (zapsano < n) { if (p->obsazeno2 == 0) p->bity[p->obsazeno1] = 0; //vynuluju nově přidělenou paměť if (n - zapsano < 8 - p->obsazeno2) { p->bity[p->obsazeno1] |= (x >> zapsano) << p->obsazeno2; p->obsazeno2 += n - zapsano; zapsano += n - zapsano; } else { p->bity[p->obsazeno1] |= ((x >> zapsano) & ((1 << (8 - p->obsazeno2)) - 1)) << p->obsazeno2; zapsano += 8 - p->obsazeno2; p->obsazeno1++; p->obsazeno2 = 0; } } } //zapíše x na konec p, víme, že x je z {a, ..., b} void zapis_z_intervalu(struct bitove_pole * p, unsigned x, unsigned a, unsigned b) { zapis_bity(p, x - a, log_2(b-a+1)); } //přidá do bitového pole hodnotu x, M je maximální hodnota //zachovává setříděnost, takže musíme přepsat celé pole, abychom mohli vložit novou hodnotu void zapis_cislo(struct bitove_pole * p, unsigned x, unsigned M) { struct bitove_pole nove; nove.alokovano = p->alokovano; nove.bity = malloc(nove.alokovano); nove.obsazeno1 = nove.obsazeno2 = 0; bool zapsano = false; unsigned min = 0; p->pozice1 = p->pozice2 = 0; while (jde_cist_bity(p)) { unsigned c = cti_z_intervalu(p, min, M); if (!zapsano && x < c) { zapis_z_intervalu(&nove, x, min, M); min = x + 1; zapsano = true; } zapis_z_intervalu(&nove, c, min, M); min = c + 1; } if (!zapsano) zapis_z_intervalu(&nove, x, min, M); free(p->bity); *p = nove; } bool get_bit(uint8_t * p, unsigned i) { //přečte bit na pozici i z pole bytů return (p[i / 8] & (1 << (i % 8))) != 0; } void set_bit(uint8_t * p, unsigned i, bool x) { //zapíše bit x na pozici i v poli bytů if (x) p[i / 8] |= (1 << (i % 8)); else p[i / 8] &= ~(1 << (i % 8)); } //hlavní funkce programu, vrací počet jedinečných znaků na vstupu unsigned jedinecnych_znaku(void) { prectene.bity = NULL; prectene.alokovano = prectene.obsazeno1 = prectene.obsazeno2 = 0; unsigned znaku = 0; bool zpusob2 = false; scanf("%u %u", &N, &K); struct znak z; z.radky = (unsigned *)malloc(N * sizeof(&z.radky)); for (unsigned i = 0; i < N; i++) z.radky[i] = N; unsigned M = cislo_znaku(z); unsigned logM = log_2(M); for (unsigned i = 0; i < K; i++) { nacti_znak(&z); unsigned c = cislo_znaku(z); if (!zpusob2) { //použijeme první způsob: seznam přečtených znaků, uložených pod svým číslem prectene.pozice1 = prectene.pozice2 = 0; unsigned min = 0; bool nalezeno = false; while (!nalezeno && jde_cist_bity(&prectene)) { unsigned c2 = cti_z_intervalu(&prectene, min, M); if (c2 == c) nalezeno = true; else min = c2 + 1; } if (!nalezeno) { znaku++; zapis_cislo(&prectene, c, M); } if (prectene.alokovano >= (M + 7) / 8) { //přejdeme na druhý způsob zpusob2 = true; uint8_t * bity = (uint8_t *)calloc((M + 7) / 8, 1); prectene.pozice1 = prectene.pozice2 = 0; unsigned min = 0; while (jde_cist_bity(&prectene)) { unsigned c = cti_z_intervalu(&prectene, min, M); set_bit(bity, c, true); min = c + 1; } free(prectene.bity); prectene.bity = bity; } } else { //používáme druhý způsob: pole bitů, když je i. bit 1, tak jsme už někdy přečetli znak s číslem i if (!get_bit(prectene.bity, c)) { znaku++; set_bit(prectene.bity, c, true); } } } free(z.radky); free(prectene.bity); return znaku; } int main(void) { printf("%u", jedinecnych_znaku()); return 0; }