Двунаправленное остовное дерево

я наткнулся на этот вопрос от interviewstreet.com

машины в очередной раз напали на королевство Сионов. Царство Xions имеет N городов и N-1 двунаправленных дорог. Дорожная сеть такой, что между любой парой городов существует уникальный путь.

Морфеус имеет Новости, что K машины планируют уничтожить все Королевство. Эти машины изначально живут в K разных города королевства и в любое время отныне они могут планировать и начать атака. Поэтому он попросил Нео уничтожить некоторые дороги, чтобы сорвать связь между машинами i.e после разрушения этих дорог не должно быть никакого пути между любыми двумя машинами.

поскольку атака может быть в любое время, Нео должен выполнить эту задачу как можно быстрее. Каждая дорога в королевстве занимает определенное время, чтобы уничтожьте их, и они могут быть уничтожены только по одному за раз.

нужно написать программу, которая сообщает Neo минимальное количество времени ему потребуется нарушить связь между машинами.

образец ввода первая строка ввода содержит два, разделенных пробелом целые числа, N и K. города пронумерованы от 0 до N-1. Далее следует N-1 строки, каждая из которых содержит три целых числа, разделенных пробелом, x y z, которые означает, что существует двунаправленная дорога, соединяющая город x и город y, и чтобы уничтожить эту дорогу, требуется z единиц времени. Далее следует K строк каждый содержит целое число. Ith integer-идентификатор города, в котором ith Машина в настоящее время находится.

формат вывода: в единственной строке выведите минимальное время, необходимое для нарушить связь между машинами.

Пример Ввода

5 3
2 1 8
1 0 5
2 4 5
1 3 4
2
4
0

Пример Вывода

10

объяснение Нео может уничтожить дорогу, соединяющую город 2 и город 4 вес 5, и дорога, соединяющая город 0 и город 1 веса 5. Как единственный дорога может быть разрушена за один раз, общее минимальное время составляет 10 единиц времени. После разрушения этих дорог ни одна из машин смогите достигнуть другую машину через любой путь.

ограничения

2 <= N <= 100,000
2 <= K <= N
1 <= time to destroy a road <= 1000,000

может кто-нибудь дать представление о том, как подойти к решению.

6 ответов


все три ответа приведут к правильному решению, но вы не можете достичь решения в течение срока, предусмотренного interviewstreet.com - ... Вы должны придумать какой-то простой подход, чтобы успешно решить эту проблему.

подсказка: начните с узла, где присутствует машина.


Tree

королевство имеет N городов, N-1 краев и полностью связано, поэтому наше королевство дерево (в теории графов). На этом рисунке вы можете увидеть древовидное представление вашего входного графа, в котором машины представлены красными вершинами.

кстати, вы должны рассмотреть все пути от корневой вершины до всех вершин. Таким образом, в каждом пути у вас будет несколько красных узлов, и при удалении ребер вы должны учитывать только соседние красные узлы. Например, в пути 0-10 есть две значимые пары - (0,3) и (3,10). И вы должны удалить ровно один узел (не меньше, не больше) из каждого пути, который связал вершины попарно.

Я надеюсь, что этот совет был полезным.


как говорили другие, связный граф с N вершинами и N-1 ребрами является деревом.

эта проблема требует жадного решения; я бы пошел на модификацию алгоритм Крускала:

начните с набора из N компонентов - 1 для каждого узла (города). Следите за тем, какие компоненты содержат город, занятый машиной.

Возьмите 1 край (дорога) за раз, заказ по убыванию веса (начиная с дорог, наиболее дорогостоящих для уничтожения). Для этого ребра (которое обязательно соединяет две составляющие-граф является деревом):

  • если оба соседних компонента содержат город, занятый машиной, эта дорога должна быть уничтожена, отметьте ее как таковую
  • в противном случае объедините соседние компоненты в один. Если один из них содержал город, занятый машиной, то и объединенный компонент тоже.

когда вы закончите со всеми ребрами, верните сумму затрат для уничтоженных дороги.

сложность будет такой же, как алгоритм Крускала, то есть почти линейной для хорошо выбранной структуры данных и метода сортировки.


pjotr имеет правильный ответ (хотя и не совсем асимптотически оптимальный), но это утверждение

эта проблема требует жадного решения

действительно требует доказательств, как и в реальном мире (в отличие от спортивного программирования), есть несколько проблем такого "рода", для которых жадное решение не является оптимальным (например, эта же проблема в общих графах, которая называется многотерминальным разрезом и является NP-жесткой). В этом случае доказательство состоит из проверки матроида аксиомы. Пусть множество ребер a &subseteq; E будет независимая если граф (V, E & setminus; A) имеет точно |A| + 1 связанные компоненты, содержащие по крайней мере одну машину.

независимость пустого набора. тривиально.

наследственного имущества. пусть A-независимый набор. Каждое ребро e &in; a соединяет две связанные компоненты графа (V, E & setminus; A) и каждая связная компонента содержит по крайней мере одну машину. При возврате e в график число связанных компонентов, содержащих по крайней мере одну машину, уменьшается на 1, поэтому &setminus; {e} также независим.

свойство увеличения. пусть A и B-независимые множества с |A|


начните выполнение DFS с любого из узлов машины. Кроме того, следите за краем с минимальным весом, встречающимся до сих пор. Как только вы найдете следующий узел, который также содержит машину, удалите записанный до сих пор минимальный край. Запустите DFS с этого нового узла. Повторяйте, пока не найдете все узлы, где существуют машины.

должно быть O (N) таким образом !!


Я пишу некоторый код и вставляю все тесты.

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

class Line {
public:
    Line(){
        begin=0;end=0;  weight=0;
}
int begin;int end;int weight;

bool operator<(const Line& _l)const {
    return weight>_l.weight;
}
};

class Point{
public:
Point(){
    pre=0;machine=false;
}
int pre;
bool machine;
};

void DP_Matrix();
void outputLines(Line* lines,Point* points,int N);

int main() {
    DP_Matrix();
    system("pause");
    return 0;
}   

int FMSFind(Point* trees,int x){
    int r=x;
    while(trees[r].pre!=r)
        r=trees[r].pre;
    int i=x;int j;
    while(i!=r) {
            j=trees[i].pre;
        trees[i].pre=r;
        i=j;
    }
return r;
}

void DP_Matrix(){
int N,K,machine_index;scanf("%d%d",&N,&K);
Line* lines=new Line[100000];
Point* points=new Point[100000];
N--;
for(int i=0;i<N;i++) {
    scanf("%d%d%d",&lines[i].begin,&lines[i].end,&lines[i].weight);
    points[i].pre=i;
}
points[N].pre=N;
for(int i=0;i<K;i++) {
    scanf("%d",&machine_index);
    points[machine_index].machine=true;
}
long long finalRes=0;
for(int i=0;i<N;i++) {
    int bP=FMSFind(points,lines[i].begin);
    int eP=FMSFind(points,lines[i].end);
    if(points[bP].machine&&points[eP].machine){
        finalRes+=lines[i].weight;
    }
    else{
        points[bP].pre=eP;
        points[eP].machine=points[bP].machine||points[eP].machine;
        points[bP].machine=points[eP].machine;
    }
}
cout<<finalRes<<endl;
delete[] lines;
delete[] points;
}

void outputLines(Line* lines,Point* points,int N){
printf("\nLines:\n");
for(int i=0;i<N;i++){
    printf("%d\t%d\t%d\n",lines[i].begin,lines[i].end,lines[i].weight);
}
printf("\nPoints:\n");
for(int i=0;i<=N;i++){
    printf("%d\t%d\t%d\n",i,points[i].machine,points[i].pre);
}
}