elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Introducción a la Factorización De Semiprimos (RSA)


  Mostrar Mensajes
Páginas: 1 2 3 4 [5] 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... 24
41  Programación / Programación C/C++ / Re: Ayuda con mi proyecto de grafos en: 29 Mayo 2011, 18:24 pm
Si se quiere conservar un camino mínimo usando Dijkstra, una manera sencilla de hacerlo es guardar para cada nodo su antecesor, de manera que cada vez que se relaja el coste mínimo de un nodo, se pone como padre o antecesor el nodo origen de la arista (o arco) empleada.

Como he puesto, con esto se guarda un camino mínimo para cada nodo, no todos en caso de que haya más de un camino mínimo que acabe en un nodo.
42  Programación / Programación C/C++ / Re: Ayuda con mi proyecto de grafos en: 29 Mayo 2011, 17:55 pm
Discrepo. Yo considero que para este caso es mejor una lista de adyacencia. El código no se complica para nada y en caso general es más eficiente que con una matriz de adyacencia para hacer un Dijkstra (pese a que con 45 nodos no se notará la mejoría).
43  Programación / Programación C/C++ / Re: TOPCODER en: 23 Mayo 2011, 14:38 pm
He participado alguna vez en Topcoder, pero no participo regularmente, para eso prefiero Codeforces, me gusta más el estilo, aunque participo habitualmente en concursos de programación y compito con mi universidad en el ICPC, de modo que estoy habituado a este tipo de competiciones.

Cuando dices que el compilador de Topcoder te los rechaza no sé si te refieres a que no te compila o que te dice que no pasa los casos de prueba.

Si es lo primero, mira en la página que versión del compilador utilizan para cada lenguaje, yo he programado en C++ y no he tenido ningún problema con el compilador. Ten en cuenta que en Topcoder, a diferencia de otros concursos, no debes hacer un programa completo que lea entrada e imprima salida, sino que debes programar una clase con el nombre y método que te digan. Yo te recomiendo programar directamente en el compilador de la página, no por versiones ni nada, simplemente que si luego tienes que pasarlo allí, pierdes mucho tiempo y la puntuación que recibes por un problema correcto depende del tiempo que tardes en resolverlo una vez lo abres.

Si es lo segundo, el problema es que tu código no hace lo que tiene que hacer. Una cosa es que localmente te funcione bien con los casos que tú pruebas y otra es que haga correctamente todos los casos privados con los que lo comprueba la página, que contienen todos los casos límite (traducción algo libre de corner cases, es decir, entradas que son problemáticas por algún motivo, como que se tengan que tratar de forma especial o que sean los peores casos que se pueda encontrar tu algoritmo en cuanto a complejidad temporal o espacial).
44  Programación / Ejercicios / Re: EJERCICIOS BASICOS C++ en: 21 Mayo 2011, 16:25 pm
Sin entrar en temas de si es mejor hacer de una forma u otra lo que comentáis, a modo de apunte comento una mejora que puede hacerse respecto al código original que son los módulos. Realizar la operación módulo es costoso, mucho más que una suma o una resta. Una técnica estándar para optimizar el código cuando se hacen módulos por potencias de dos es sustituir los módulos por ANDs bit a bit. Puede parecer una tontería, pero puede mejorar mucho el tiempo de ejecución de un programa.

La norma general sería convertir esto:
Código:
a%pot
en esto otro:
Código:
a&(pot - 1)
45  Programación / Programación C/C++ / Re: estructura doblemente encadenada en: 15 Mayo 2011, 18:59 pm
Simplemente hace un intercambio de las variables:
http://www.cplusplus.com/reference/algorithm/swap/
46  Programación / Programación C/C++ / Re: estructura doblemente encadenada en: 14 Mayo 2011, 18:51 pm
Tienes varios errores en tu código.

En la función para añadir al inicio, creas un nuevo nodo que pones como inicio, pero no haces que la lista anterior apunte a él (sólo el nuevo nodo apunta a lista). Básicamente, falta la línea que tienes comentada, que supongo que lo habrás hecho porque te daba Segmentation Fault. Esto sucedería porque al principio, a_inici es NULL, de forma que haría falta un if para comprobarlo.

La función de añadir al final la veo horrible por un simple motivo. Estás guardando un puntero al inicio de la lista, pero no uno para el final. Así, si quieres añadir al algo al final, tienes que recorrer la lista entera para llegar al final y después añadir: pasas una operación que teniendo un puntero extra sería O(1) a una operación O(n), siendo n el número de elementos de la lista.

La función para mirar si está el elemento parece correcta, mientras que la de borrar no entiendo qué haces, pero no veo que recorras la lista, de forma que dudo que sea correcta.

He picado ahora cómo haría yo las funciones que implementas, no lo he probado en profundidad, pero parece que funcionan bien.
Código
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. struct Node {
  5.    int dada;
  6.    Node *seguent;
  7.    Node *anterior;
  8. };
  9.  
  10. Node *inici = NULL, *final = NULL;
  11.  
  12. void afegirInici(int x) {
  13.    Node *p = new Node;
  14.    p->dada = x;
  15.    p->seguent = inici;
  16.    p->anterior = NULL;
  17.    if (inici != NULL) inici->anterior = p;
  18.    swap(p, inici);
  19.    if (final == NULL) final = inici;
  20. }
  21.  
  22. void afegirFinal(int x) {
  23.    Node *p = new Node;
  24.    p->dada = x;
  25.    p->seguent = NULL;
  26.    p->anterior = final;
  27.    if (final != NULL) final->seguent = p;
  28.    swap(p, final);
  29.    if (inici == NULL) inici = final;
  30. }
  31.  
  32. bool existeix(int x) {
  33.    Node *actual = inici;
  34.    while (actual != NULL) {
  35.        if (actual->dada == x) return true;
  36.        actual = actual->seguent;
  37.    }
  38.    return false;
  39. }
  40.  
  41. //Esborra totes les ocurrencies de x a la llista
  42. void esborrar(int x) {
  43.    Node *actual = inici;
  44.    while (actual != NULL) {
  45.        if (actual->dada == x) {
  46.            if (actual->anterior != NULL) actual->anterior->seguent = actual->seguent;
  47.            else inici = actual->seguent;
  48.            if (actual->seguent != NULL) actual->seguent->anterior = actual->anterior;
  49.            else final = actual->anterior;
  50.  
  51.            Node *aux = actual->seguent;
  52.            delete actual;
  53.            actual = aux;
  54.        }
  55.        else actual = actual->seguent;
  56.    }
  57. }
  58.  
  59. void llistar() {
  60.    Node *actual = inici;
  61.    while (actual != NULL) {
  62.        cout << actual->dada;
  63.        if (actual->seguent != NULL) cout << " <-> ";
  64.        actual = actual->seguent;
  65.    }
  66.    cout << endl;
  67. }
  68.  
  69. int main() {
  70.    char c;
  71.    while (cin >> c) {
  72.        if (c == 'I') {
  73.            int x;
  74.            cin >> x;
  75.            afegirInici(x);
  76.        }
  77.        else if (c == 'F') {
  78.            int x;
  79.            cin >> x;
  80.            afegirFinal(x);
  81.        }
  82.        else if (c == 'E') {
  83.            int x;
  84.            cin >> x;
  85.            cout << (existeix(x)?"Existeix":"No existeix") << endl;
  86.        }
  87.        else if (c == 'L') llistar();
  88.        else {
  89.            int x;
  90.            cin >> x;
  91.            esborrar(x);
  92.        }
  93.    }
  94. }

Te pongo la prueba que he hecho:

Entrada:
Código:
I 1 I 2 I 3
L
F 1
L
F 23
L
E 1
E 23
E 25
E 4
B 2
L
B 1
L
B 3
L
B 23
L
I 1
L
F 2
L

Salida:
Código:
3 <-> 2 <-> 1
3 <-> 2 <-> 1 <-> 1
3 <-> 2 <-> 1 <-> 1 <-> 23
Existeix
Existeix
No existeix
No existeix
3 <-> 1 <-> 1 <-> 23
3 <-> 23
23

1
1 <-> 2
47  Programación / Programación C/C++ / Re: Retos C/C++ en: 9 Mayo 2011, 09:54 am
Tu solución parece que es incorrecta. La he probado y comparado con las tres mías y para casos pequeños dan lo mismo, pero a poco que los números empiezan a ser grandes falla y las mías coinciden.

El problema creo que es que no aplicas correctamente el módulo. Si he entendido tu solución, cada vez que te pasas del módulo restas hasta compensarlo (o lo que sería equivalente, haces % c). Esto sería correcto si no hubiera divisiones de por medio, pero al haber divisiones falla. No puedes dividir y hacer módulo a la vez, puesto que no es lo mismo. Lo correcto sería en lugar de dividir, multiplicar por el inverso del divisor módulo 1000000000, pero no es primo, de forma que no todos los divisores serán coprimos con él, de forma que no puedes solucionar el problema así...

Si miras mi solución, al final acabo calculando (n +m sobre m) - 1 mod 1000000000, pero para realizarlo, en lugar de usar la típica fórmula con factoriales (que implica divisiones) uso la recurrencia lineal que solo tiene sumas, que no dan problemas con el módulo, haciendo pasar una solución que matemáticamente es lineal a ser cuadrática con memoria cuadrática también.
48  Programación / Programación C/C++ / Re: Entero de 10 elevado a 1000 en: 8 Mayo 2011, 20:15 pm
Más que strings, para programar BigInts la práctica más extendida es usar vectores de enteros, de forma que en cada posición no se guarda un único dígito, sino varios de ellos, permitiendo aprovechar las operaciones de las variables nativas del lenguaje y además haciendo el cálculo de forma mucho más eficiente.

He picado un ejemplo para poder hacer potencias. No lo he testeado así que quizá tenga algún bug, pero la idea en sí creo que se entiende con el código.
Código
  1. #include <iostream>
  2. #include <vector>
  3. #include <sstream>
  4. #include <iomanip>
  5. using namespace std;
  6.  
  7. typedef long long ll;
  8. typedef vector<ll> VL;
  9.  
  10. const ll base = 1000000000;
  11.  
  12. class BigInt {
  13.    public:
  14.        VL v;
  15.        int signo;
  16.  
  17.        BigInt() {
  18.            v = VL(1, 0);
  19.            signo = 1;
  20.        }
  21.        BigInt(ll x) {
  22.            do {
  23.                v.push_back(x%base);
  24.                x /= base;
  25.            } while (x > 0);
  26.            signo = 1;
  27.        }
  28.        void operator*=(const BigInt& b) {
  29.            BigInt c;
  30.            c.v.resize(v.size() + b.v.size(), 0);
  31.            c.signo = signo*b.signo;
  32.            for (int i = 0; i < v.size(); ++i) {
  33.                for (int j = 0; j < b.v.size(); ++j) {
  34.                    int k = i + j;
  35.                    c.v[k] += v[i]*b.v[j];
  36.                    while (c.v[k] >= base) {
  37.                        c.v[k + 1] += c.v[k]/base;
  38.                        c.v[k++] %= base;
  39.                    }
  40.                }
  41.            }
  42.            int i;
  43.            for (i = c.v.size() - 1; i > 0 and c.v[i] == 0; --i);
  44.            c.v.resize(i + 1);
  45.            *this = c;
  46.        }
  47.        BigInt pow(ll k) {
  48.            if (k == 0) return BigInt(1);
  49.            BigInt res = this->pow(k >> 1);
  50.            res *= res;
  51.            if (k&1) res *= (*this);
  52.            return res;
  53.        }
  54.        friend ostream &operator<<(ostream &out, BigInt &b) {
  55.            if (b.signo < 0) out << '-';
  56.            int i = b.v.size() - 1;
  57.            out << b.v[i];
  58.            for (--i; i >= 0; --i) out << setw(9) << setfill('0') << b.v[i];
  59.            return out;
  60.        }
  61. };
  62.  
  63. int main() {
  64.    BigInt x(10);
  65.    x = x.pow(1000);
  66.    cout << x << endl;
  67. }
  68.  
49  Programación / Programación C/C++ / Re: Retos C/C++ en: 1 Mayo 2011, 01:34 am
Como veo que la cosa está bastante parada, me tomo la libertad de poner mi análisis para el primer problema a ver si así se reactiva esto un poco, que lo considero una interesante idea. Pongo el texto en Latex a continuación:
Código:
\documentclass[a4paper,article,10pt]{article}
\usepackage{geometry}
\ttfamily
\usepackage[dvips]{graphicx,psfrag,overpic,color}
\usepackage{latexsym}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amsthm}
\usepackage{pstricks}
\usepackage[postscript]{ucs}
\usepackage[utf8]{inputenc}
\usepackage[spanish]{babel}
\usepackage{fancyhdr}
\usepackage{listings}
\geometry{margin=1.5cm}
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead{}
\fancyfoot{}
\rhead{\thepage}
\chead{Espirales - ghastlyX}
\rfoot{\thepage}

\newtheorem{teo}{{Teorema}}
\newtheorem{recu}{{Recurrencia}}
\newtheorem*{lema}{{Lema}}
\newtheorem*{coro}{{Corolario}}
\newenvironment{demo}[1][Demostración:]{\begin{trivlist}
\item[\hskip \labelsep {\bfseries #1}]}{\end{trivlist}}
\newenvironment{defi}[1][Definición]{\begin{trivlist}
\item[\hskip \labelsep {\bfseries #1}]}{\end{trivlist}}
\newenvironment{exemple}[1][Ejemplo]{\begin{trivlist}
\item[\hskip \labelsep {\bfseries #1}]}{\end{trivlist}}
\newenvironment{remark}[1][Remarca]{\begin{trivlist}
\item[\hskip \labelsep {\bfseries #1}]}{\end{trivlist}}
\renewcommand{\labelenumi}{(\alph{enumi})}

\begin{document}
\noindent Se va a proceder paso a paso en la resoluci\'on de este problema.
Dejando a un lado la soluci\'on por backtracking, puesto que es demasiado
ineficiente, se puede considerar una primera soluci\'on usando programaci\'on din\'amica.
Basta hacer un par de consideraciones para llegar a esta primera
soluci\'on. Sea $f(n, m)$ el n\'umero de espirales diferentes (sin tomar m\'odulo) en un tablero de $n\times m$.
\begin{lema}
Sean $n, m\in \mathbb{N}$ arbitrarios. Se cumple que $f(n, m) = f(m, n)$.
\end{lema}
\begin{demo}
Es evidente por simetr\'ia. $\blacksquare$
\end{demo}

\noindent Visto esto, dado un tablero de $n\times m$, las posibles espirales
son las que realizan una \textbf{L} y se acaban y las que despu\'es de realizarla
siguen avanzando. Esta \textbf{L} primero puede avanzar $n$ casillas y despu\'es
puede bajar hasta $m$ casillas, dando un total de $nm$ posibles \textbf{L}s.
Teniendo en cuenta que tras realizar la \textbf{L} vuelve a quedar un tablero de
$i\times j$, donde $i + 1 \leq n$ y $j + 1 \leq m$ son las dimensiones de
dicha \textbf{L}, se obtiene una primera recurrencia:

\begin{recu}
Sean $n, m\in \mathbb{N}$ arbitrarios. Se cumple que
$f(n, m) = nm + \sum\limits_{i = 1}^{n}\sum\limits_{j = 1}^{m}f(i, j)$.
\end{recu}
\noindent Con esta recurrencia se obtiene un primer c\'odigo, cuya complejidad es $O(n^2m^2)$:
\lstset{language=C++}
\begin{lstlisting}
#include <iostream>
#include <vector>
using namespace std;

const long long MOD = 1000000000;
long long M[1001][1001]; //Tabla para la dinamica

long long f(int n, int m) {
    if (M[n][m] == -1) { //Si no esta calculado
        //Si el simetrico esta calculado, se devuelve ese por el lema
        if (M[m][n] != -1) return M[n][m] = M[m][n];
        M[n][m] = n*m% MOD; //Espirales en forma de L sin continuar
        //Se suma el resto de espirales
        for (int i = 1; i <= n - 1; ++i)
            for (int j = 1; j <= m - 1; ++j)
                M[n][m] = (M[n][m] + f(i,j))%MOD;
    }
    return M[n][m];
}

int main() {
    int n, m;
    cin >> n >> m;
    //Inicializacion de la tabla a -1
    for (int i = 0; i <= 1000; ++i) for (int j = 0; j <= 1000; ++j) M[i][j] = -1;
    cout << f(n, m)%MOD << endl;
}
\end{lstlisting}
\noindent Esta recurrencia puede simplificarse, para as\'i eliminar uno de los
dos bucles de cada estado en la din\'amica. Si en lugar de considerar la
\textbf{L}, se avanza tan solo en la primera direcci\'on, se tienen $n$ posibles
espirales m\'as las que luego siguen creciendo en el tablero que queda, de
dimensiones $i\times (m - 1)$, donde $1 \leq i \leq n$ es lo que se avanza antes
de seguir. Con esto se obtiene la segunda recurrencia:
\begin{recu}
Sean $n, m\in \mathbb{N}$ arbitrarios. Se cumple que
$f(n, m) = n + \sum\limits_{i = 1}^{n}f(i, m - 1)$.
\end{recu}
\noindent Esta segunda recurrencia permite conseguir una complejidad de $O(n^2m)$.
El siguiente c\'odigo muestra como usarla adaptando el primer c\'odigo:
\lstset{language=C++}
\begin{lstlisting}
#include <iostream>
#include <vector>
using namespace std;

const long long MOD = 1000000000;
long long M[1001][1001];

long long f(int n, int m) {
    if (M[n][m] == -1) {
        if (m == 1) return M[n][m] = n;
        if (M[m][n] != -1) return M[n][m] = M[m][n];
        M[n][m] = n% MOD; //Posibles inicios de la L
        //Suma del resto de espirales
        for (int i = 1; i <= n; ++i)
            M[n][m] = (M[n][m] + f(i, m - 1))%MOD;
    }
    return M[n][m];
}

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i <= 1000; ++i) for (int j = 0; j <= 1000; ++j) M[i][j] = -1;
    cout << f(n, m)%MOD << endl;
}
\end{lstlisting}
\noindent Si bien este c\'odigo es bastante m\'as r\'apido que el primero,
se puede mejorar a\'un m\'as trabajando un poco m\'as la recurrencia. El siguiente
resultado muestra como poder hacerlo.
\begin{recu}
Sean $n, m\in \mathbb{N}$ arbitrarios. Se cumple que $f(n, m) = f(n - 1, m) + f(n, m - 1) + 1$.
\end{recu}
\begin{demo}
Queremos ver que $f(n, m) = f(n - 1, m) + f(n, m - 1) + 1$.
Aplicando la segunda recurrencia, se tiene que ser\'a cierto si y s\'olo si $n + \sum\limits_{i =
1}^{n}f(i, m - 1) = n - 1 + \sum\limits_{i = 1}^{n - 1}f(i, m - 1) + n + \sum\limits_{i = 1}^{n}f(i, m - 2 ) + 1$
es cierto. Simplificando esta segunda
expresi\'on queda $f(n, m - 1) = n + \sum\limits_{i = 1}^{n}f(i, m - 2)$,
que es cierta, puesto que es precisamente la segunda recurrencia vista
anteriormente. $\blacksquare$
\end{demo}
\noindent Por esta recurrencia y el lema se obtiene el \'ultimo resultado:
\begin{coro}
Sean $n, m\in \mathbb{N}$ arbitrarios. Se cumple que $f(n, m) = \binom{n+m}{m} - 1$.
\end{coro}
\noindent Por \'ultimo, el c\'odigo que utiliza este resultado:
\lstset{language=C++}
\begin{lstlisting}
#include <iostream>
using namespace std;

int M[2002][2002];
const int MOD = 1000000000;

int main() {
    int n, m;
    for (int i = 0; i < 2002; ++i) M[i][0] = M[i][i] = 1;
    for (int i = 2; i < 2002; ++i)
        for (int j = 1; j < i; ++j) 
            M[i][j] = (M[i - 1][j - 1] + M[i - 1][j])%MOD;
    cin >> n >> m;
    cout << (M[n + m][m] + MOD - 1)%MOD << endl;
}
\end{lstlisting}
A continuaci\'on se puede ver una comparativa de los diferentes c\'odigos con diferentes entradas:\\\\
\begin{tabular}{|c|c|c|c|c|}
\hline
& $n=m=100$ & $n=m=500$ & $n=m=750$& $n=m=1000$ \\
\hline
C\'odigo 1 & 0m0.288s & 2m31.501s & 12m44.252s & No realizado\\
\hline
C\'odigo 2 & 0m0.012s & 0m0.492s & 0m1.688s & 0m4.280s \\
\hline
C\'odigo 3 & 0m0.032s & 0m0.032s & 0m0.032s & 0m0.032s\\
\hline
\end{tabular}
\end{document}
50  Programación / Programación C/C++ / Re: Estructuras en arbol en: 9 Abril 2011, 16:59 pm
Como ya te han dicho, los árboles se suelen hacer de un determinado tipo según el problema que quieras resolver. Por ponerte un ejemplo de cómo sería un árbol de 3 hijos por nodo, te he picado un código para una posible implementación de un TST (Ternary Search Tree).
Código
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. struct Nodo {
  5.    char c;
  6.    Nodo *lo, *eq, *hi;
  7. };
  8.  
  9. void add(Nodo *&nodo, string& s, int p) {
  10.    if (p == s.size()) return;
  11.    if (nodo == NULL) {
  12.        nodo = new Nodo;
  13.        nodo->c = s[p];
  14.        nodo->lo = nodo->hi = nodo->eq = NULL;
  15.        add(nodo->eq, s, p + 1);
  16.    }
  17.    else {
  18.        if (s[p] < nodo->c) add(nodo->lo, s, p);
  19.        else if (s[p] > nodo->c) add(nodo->hi, s, p);
  20.        else add(nodo->eq, s, p + 1);
  21.    }
  22. }
  23.  
  24. bool search(Nodo *nodo, string& s, int p) {
  25.    if (p == s.size()) return true;
  26.    if (nodo == NULL) return false;
  27.    if (s[p] < nodo->c) return search(nodo->lo, s, p);
  28.    else if (s[p] > nodo->c) return search(nodo->hi, s, p);
  29.    else return search(nodo->eq, s, p + 1);
  30. }
  31.  
  32. int main() {
  33.    Nodo *raiz = NULL;
  34.    char c;
  35.    while (cin >> c) {
  36.        if (c == 'A') {
  37.            string s;
  38.            cin >> s;
  39.            add(raiz, s, 0);
  40.        }
  41.        else if (c == 'S') {
  42.            string s;
  43.            cin >> s;
  44.            if (search(raiz, s, 0)) cout << "String encontrada" << endl;
  45.            else cout << "String NO encontrada" << endl;
  46.        }
  47.    }
  48. }

No es difícil poner n hijos a un árbol, pero hay que tener en cuenta si sirve para algo hacerlo. Hay estructuras de tipo árbol más díficiles de implementar que otras, pero la dificultad no suele estar en el número de hijos, sino en la morfología del árbol. Por poner un par de ejemplos de algunos árboles más díficiles de picar, tienes por ejemplo los Suffix Trees o los TST con funciones de fallo para hacer un Aho-Corasick.
Páginas: 1 2 3 4 [5] 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... 24
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines