Cod sursa(job #2962654)

Utilizator anne_marieMessner Anne anne_marie Data 8 ianuarie 2023 22:25:51
Problema ADN Scor 60
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 5.15 kb
/*Nodurile:
X : C A A A A B C D E
Y : D E A C C C
Muchiile:
X -> Y : 2 (sf lui X cu în lui Y)
Y -> X : 1 (sf lui Y cu în lui X)
Caut o parcurgere a grafului astfel încât suma muchiilor sa fie maxima și fiecare nod parcurs o singura data

        dp[i][conf] = suma maxima a muchiilor pana în i având vizitate nodurile din conf
        dp[i][conf] = 0 (inițial)
Conf_nou += 1 << vec
        dp[vec][conf_nou] = max(dp[vec][conf_nou], dp[i][conf] + muchie(i, vec))

For pt conf (de la 0 la 2^n -1)
For pt I (de la 0, la n - 1)
verify dacă i în conf
for(auto vec: ad[I])
verify faca vec nu e in conf
        recurenta
*/
#include <iostream>
#include <fstream>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;
ifstream fin("adn.in");
ofstream fout("adn.out");


int n;
string adn[20];
int cost[20][20];
int dp[20][1 << 20];
vector<string> new_adn;
bool eliminated[20];
pair<int, int> father[20][1 << 20];
vector<int> path;

// ------------------------------ Word Distance ---------------------------------
int wordDistance(int word1, int word2) {
    // Calculez subsecventa comuna intre sfarsitul primului cuvant si inceputul celui de-al doilea cuavnt
    string str1 = adn[word1];
    string str2 = adn[word2];
    int w_size1 = str1.size();
    int w_size2 = str2.size();

    for (int i = 0; i < w_size1; i++) {
        // Verific suprapunerea de la pozitia i in dreapta
        bool same = true;

        for (int j = i; (j < w_size1) && (j - i < w_size2); j++) {
            // Verific daca cele doua cuvinte sunt identice
            if (str1[j] != str2[j - i]) {
                same = false;
                break;
            }
        }

        if (same) {
            // Verific daca al doilea cuvant se gaseste in interiorul primului
            if (w_size1 - i >= w_size2) {
                return -1;
            }
            // Lungimea comuna a celor doua cuvinte
            return w_size1 - i;
        }
    }
    // Cuvintele nu au litere in comun
    return 0;
}




// ------------------------------ Remake ADN ---------------------------------
void remakeADN() {
    new_adn.push_back("");
    // Daca sunt mai multe cuvinte identice, lasa doar prima aparitie
    for(int i = 1; i < n; i ++)
        for(int j = i + 1; j <= n; j ++) {
            if(adn[i] == adn[j])
                eliminated[j] = true;
        }
    for(int i = 1; i < n ; i ++)
        for(int j = i + 1; j <= n; j ++) {
            if(eliminated[i] || eliminated[j])
                continue;
            if(wordDistance(i, j) == -1) {
                eliminated[j] = true;
                //cout << i << " " << j <<'\n';
            } else
                if(wordDistance(j, i) == -1) {
                    eliminated[i] = true;
                    //cout << j << " " << i << '\n';
                }
        }
    for(int i = 1; i <= n; i ++)
        if(!eliminated[i]) {
            new_adn.push_back(adn[i]);
        }
}


// ------------------------------ Main ---------------------------------
int main() {

    // Citesc string-urile de adn
    fin >> n;
    for(int i = 1; i <= n; i ++)
        fin >> adn[i];

    // Refac vectorul de string-uri astfel incat sa elimin string-urile ce se regasesc in interiorul altora
    remakeADN();
    n = new_adn.size() - 1;

    // Suprascriu adn cu new_adn
    for(int i = 1; i <= n ; i ++)
        adn[i] = new_adn[i];

    // Calculez costul distantelor dintre cuvinte
    for(int i = 1; i < n; i ++)
        for(int j = i + 1; j <= n; j ++) {
            cost[i][j] = wordDistance(i, j);
            cost[j][i] = wordDistance(j, i);
        }

    // Caut o parcurgere a grafului astfel încât suma muchiilor sa fie maxima și fiecare nod parcurs o singura data
    // dp[i][conf] = suma maxima a muchiilor pana în i având vizitate nodurile din conf
    for(int conf = 0; conf <= (1 << n) - 1; conf ++)
        for(int i = 0; i < n; i ++)
            if(conf & (1 << i)) // Verific daca i este deja vizitat
                for(int j = 0; j < n; j ++)
                    if(cost[i + 1][j + 1] != 0 && !(conf & (1 << j))) { // Verific daca j este vecini si daca nu este vizitat
                        int new_conf = conf + (1 << j);
                        if(dp[j][new_conf] < dp[i][conf] + cost[i + 1][j + 1])
                            father[j][new_conf] = make_pair(i, conf);
                        dp[j][new_conf] = max(dp[j][new_conf], dp[i][conf] + cost[i + 1][j + 1]);

                    }

    int last_node = -1;
    int max_size = -1;
    for(int i = 0; i <= n; i ++)
        if(dp[i][(1 << n) - 1] > max_size) {
            max_size = dp[i][(1 << n) - 1];
            last_node = i;
        }

    int node = last_node;
    int conf = (1 << n) - 1;

    while(conf != 0) {
        path.push_back(node + 1);
        int new_conf = father[node][conf].second;
        node = father[node][conf].first;
        conf = new_conf;
    }

    reverse(path.begin(), path.end());

    // Reconstituire sir
    string result = "";
    result += adn[path[0]];
    for(int i = 1; i < path.size(); i ++) {
        for(int j = cost[path[i - 1]][path[i]]; j <= adn[path[i]].size() - 1; j ++) {
            result += adn[path[i]][j];
        }
    }
    fout << result;
    return 0;
}