题目描述
n个节点和n - 1条双向边组成了一棵树。这些边分为两种,一种类型为1,一种类型为2,类型为2的边需要维修。当我们选中一个节点x时,节点x到节点1路径上的所有类型为2的边都会被维修。
我们需要找到n个节点的一个子集,满足 子集中个数最少,并且所有类型为2的边都得到维修
分析
由于输入只是两点相连,并没有说明是父子关系,所以首先搜索处理出树的结构,标记需要处理的节点,然后从下向上搜索,消除路途上的标记。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <queue>
#include <map>
using namespace std;
int a[100010],n,num[100010],vis[100010];
vector <int> edge[100010];
queue <pair<int,int> > p;
void dfs(int x)
{
if(x == 1 || vis[x])
return;
vis[x]++;
int t = a[x];
if(num[t])
num[t] = 0;
dfs(t);
}
void dfs1(int x)
{
vis[x]++;
for(int i = 0; i < edge[x].size(); i++)
{
int k = edge[x][i];
if(!vis[k])
{
a[k] = x;
dfs1(k);
}
}
}
int main()
{
int t,C = 1;
//scanf("%d",&t);
while(scanf("%d",&n) != EOF)
{
int m = 0,k = 0,u,v,w;
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
memset(edge,0,sizeof(edge));
while(!p.empty())
p.pop();
for(int i = 0; i < n-1; i++)
{
scanf("%d%d%d",&u,&v,&w);
edge[u].push_back(v);
edge[v].push_back(u);
if(w == 2)
p.push(make_pair(u,v));
}
dfs1(1);
while(!p.empty())
{
int x = p.front().first;
int y = p.front().second;
p.pop();
if(a[x] == y)
num[x]++;
else
num[y]++;
}
memset(vis,0,sizeof(vis));
for(int i = n; i > 0; i--)
{
if(num[i])
dfs(i);
}
for(int i = 0; i <= n; i++)
if(num[i])
k++;
printf("%d\n",k);
for(int i = n; i > 0; i--)
if(num[i])
printf("%d ",i);
printf("\n");
}
}