Otimizando a Construção de Grafos com Busca Memorizada

O código original, que utiliza uma abordagem de O(n²) para construir o grafo, resultou em um tempo de execução de 772ms. A principal causa dessa complexidade é a maneira como as arestas são adicionadas entre os nós.


#include<bits>
#define int long long
using namespace std;
const int N=1e6+10,M=1e4+10;
int n,m,res,f[N],p[N],a[N],s,k,level[N];
int e[N],ne[N],h[N],idx;
bool vis[N],mp[M][M];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void topsort()
{
    queue<int>que;
    for(int i=1;i<=n;i++) if(!p[i]) que.push(i);
    while(!que.empty()){
        int now=que.front(); que.pop();
        for(int i=h[now];~i;i=ne[i]){
            int j=e[i];
            p[j]--;
            if(p[j]==0) level[j]=level[now]+1,que.push(j);
        }
    }
}

void spfa()
{
    queue<int>que;
    que.push(0),level[0]=0,vis[0]=true;
    while(!que.empty()){
        int now=que.front(); que.pop();
        vis[now]=false;
        for(int i=h[now];~i;i=ne[i]){
            int j=e[i];
            if(level[j]<level cin="" dfs="" for="" i="h[u];~i=ne[i])" if="" int="" level="" main="" return="" signed="" std::ios::sync_with_stdio="" u="" vis="">>n>>m;
    memset(h,-1,sizeof h);
    for(int i=0;i<m cin="">>k;
        memset(vis,false,sizeof vis);
        for(int j=0;j<k cin="">>a[j],vis[a[j]]=true;
        for(int j=a[0];j<=a[k-1];j++){
            if(vis[j]) continue;
            for(int e=0;e<k cout="" for="" i="1;i<=n;i++)" if="" memset="" mp="" res="max(res,dfs(i));" return="" vis=""></k></k></m></level></int></int></bits>

Otimização com Ponto Virtual

Uma otimização significativa pode ser alcançada ao introduzir um ponto virtual. Em vez de conectar diretamente todos os elementos de um conjunto a todos os elementos fora dele (o que leva a O(n²) arestas em casos extremos), podemos adicionar um ponto virtual intermediário. Isso reduz a complexidade da adição de arestas para O(n) por conjunto, resultando em um total de O(nm) arestas e melhorando drasticamente o tempo de execução.

Com essa abordagem, o tempo de execução foi reduzido para 244ms, com o uso de apenas 13.34MB de memória e 0.69KB de código.

A lógica principal é a seguinte:

  • Para cada conjunto de elementos fornecido, criamos um ponto virtual (n + i, onde i é o índice do conjunto).
  • Adicionamos arestas do ponto virtual para cada eelmento dentro do conjunto.
  • Adicionamos arestas de cada elemento fora do conjunto para o ponto virtual correspondante.
  • A função DFS, ao calcular o nível de um nó, agora considera a subtração de 1 se o nó for um ponto virtual, garnatindo que apenas os nós originais contribuam para o comprimento do caminho.

#include<bits>
#define int long long
using namespace std;
const int N=1e6+10,M=1e4+10;
int n,m,res,f[N],p[N],a[N],s,k,level[N];
int e[N],ne[N],h[N],idx;
bool vis[N],mp[M][M]; // mp não é mais usado na versão otimizada
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

// topsort e spfa não são diretamente usados na solução final, mas são mantidos para referência
void topsort()
{
    queue<int>que;
    for(int i=1;i<=n;i++) if(!p[i]) que.push(i);
    while(!que.empty()){
        int now=que.front(); que.pop();
        for(int i=h[now];~i;i=ne[i]){
            int j=e[i];
            p[j]--;
            if(p[j]==0) level[j]=level[now]+1,que.push(j);
        }
    }
}

void spfa()
{
    queue<int>que;
    que.push(0),level[0]=0,vis[0]=true;
    while(!que.empty()){
        int now=que.front(); que.pop();
        vis[now]=false;
        for(int i=h[now];~i;i=ne[i]){
            int j=e[i];
            if(level[j]<level calculado="" dfs="" foi="" for="" i="h[u];~i;i=ne[i])" if="" int="" j="" level="" max_level="0;" memorizado="" o="" ponto="" retorna="" return="" se="" u="" um="" valor="" virtual="" vis=""> n), não contamos ele como um nível real no caminho final.
    // A subtraçao é feita aqui caso o nó virtual nao tenha saido do loop
    if(u > n) level[u]--;
    return level[u];
}
signed main()
{
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=1;i<=m;i++){ // Loop pelos 'm' conjuntos
        cin>>k;
        memset(vis,false,sizeof vis);
        for(int j=1;j<=k;j++) cin>>a[j],vis[a[j]]=true,add(n+i,a[j]); // Adiciona arestas do ponto virtual (n+i) para cada elemento no conjunto
        for(int j=a[1];j<=a[k];j++){ // Itera sobre o intervalo dos elementos do conjunto
            if(vis[j]) continue; // Pula se o elemento já faz parte do conjunto atual
            add(j,n+i); // Adiciona aresta do elemento para o ponto virtual (n+i)
        }
    }

    // O cálculo do resultado final é feito através da DFS a partir dos nós que não têm dependências de entrada (nível 0 implícito)
    for(int i=1;i<=n;i++) {
        // Verificamos se o nível já foi calculado (pode acontecer se for alcançado por outro nó)
        if(!level[i]) {
            res=max(res,dfs(i));
        }
    }
    cout<<res ajuste="" ap="" considera="" corretos="" da="" dfs="" j="" n="" o="" os="" para="" pontos="" resultado="" return="" virtuais=""></res></level></int></int></bits>

Tags: Algoritmos em Grafos Otimização busca em profundidade programação dinâmica pontos virtuais

Publicado em 6-9 23:36 por Thomas