236 lines
5.0 KiB
C++
236 lines
5.0 KiB
C++
/* Check cf5-opt.vim defs.
|
|
VIM: let g:lcppflags="-std=c++11 -O2 -pthread"
|
|
VIM: let g:wcppflags="/O2 /EHsc /DWIN32"
|
|
VIM: let g:cppflags=g:Iboost.g:Itbb
|
|
VIM: let g:ldflags=g:Lboost.g:Ltbb.g:tbbmalloc.g:tbbmproxy
|
|
VIM: let g:ldlibpath=g:Bboost.g:Btbb
|
|
VIM: let g:argv=""
|
|
*/
|
|
|
|
/*
|
|
Problem B. Full Binary Tree
|
|
This contest is open for practice. You can try every problem as many times as you like, though we won't keep track of which problems you solve. Read the Quick-Start Guide to get started.
|
|
Small input
|
|
9 points
|
|
Judge's response for last submission: Correct.
|
|
Large input
|
|
21 points
|
|
Judge's response for last submission: Correct.
|
|
Problem
|
|
|
|
A tree is a connected graph with no cycles.
|
|
|
|
A rooted tree is a tree in which one special vertex is called the root. If there is an edge between X and Y in a rooted tree, we say that Y is a child of X if X is closer to the root than Y (in other words, the shortest path from the root to X is shorter than the shortest path from the root to Y).
|
|
|
|
A full binary tree is a rooted tree where every node has either exactly 2 children or 0 children.
|
|
|
|
You are given a tree G with N nodes (numbered from 1 to N). You are allowed to delete some of the nodes. When a node is deleted, the edges connected to the deleted node are also deleted. Your task is to delete as few nodes as possible so that the remaining nodes form a full binary tree for some choice of the root from the remaining nodes.
|
|
Input
|
|
|
|
The first line of the input gives the number of test cases, T. T test cases follow. The first line of each test case contains a single integer N, the number of nodes in the tree. The following N-1 lines each one will contain two space-separated integers: Xi Yi, indicating that G contains an undirected edge between Xi and Yi.
|
|
Output
|
|
|
|
For each test case, output one line containing "Case #x: y", where x is the test case number (starting from 1) and y is the minimum number of nodes to delete from G to make a full binary tree.
|
|
Limits
|
|
|
|
1 ≤ T ≤ 100.
|
|
1 ≤ Xi, Yi ≤ N
|
|
Each test case will form a valid connected tree.
|
|
Small dataset
|
|
|
|
2 ≤ N ≤ 15.
|
|
Large dataset
|
|
|
|
2 ≤ N ≤ 1000.
|
|
Sample
|
|
|
|
Input
|
|
|
|
Output
|
|
|
|
|
|
3
|
|
3
|
|
2 1
|
|
1 3
|
|
7
|
|
4 5
|
|
4 2
|
|
1 2
|
|
3 1
|
|
6 4
|
|
3 7
|
|
4
|
|
1 2
|
|
2 3
|
|
3 4
|
|
|
|
|
|
|
|
Case #1: 0
|
|
Case #2: 2
|
|
Case #3: 1
|
|
|
|
In the first case, G is already a full binary tree (if we consider node 1 as the root), so we don't need to do anything.
|
|
|
|
In the second case, we may delete nodes 3 and 7; then 2 can be the root of a full binary tree.
|
|
|
|
In the third case, we may delete node 1; then 3 will become the root of a full binary tree (we could also have deleted node 4; then we could have made 2 the root).
|
|
*/
|
|
#include <assert.h>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <exception>
|
|
#include <stdexcept>
|
|
#include <map>
|
|
#include <set>
|
|
#include <list>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <memory>
|
|
#include <functional>
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <limits>
|
|
#include <math.h>
|
|
|
|
using namespace std;
|
|
|
|
typedef std::vector<std::pair<int,int>> vec;
|
|
|
|
typedef std::vector<std::vector<int>> ss;
|
|
typedef std::vector<std::vector<int>> edges;
|
|
typedef std::vector<std::pair<int,int>> vec;
|
|
|
|
int max_tree( ss& t, int i, int r )
|
|
{
|
|
if ( t[i].size() == 2 && r!=0 )
|
|
return 1;
|
|
|
|
int m1 = 0;
|
|
int m2 = 0;
|
|
|
|
for ( auto j: t[i] )
|
|
{
|
|
if ( j != r )
|
|
{
|
|
int mt = max_tree( t, j, i );
|
|
if ( m1 < m2 )
|
|
swap( m1, m2 );
|
|
m2 = max(m2,mt);
|
|
}
|
|
}
|
|
|
|
return m1+m2+1;
|
|
}
|
|
|
|
int check2(ss& t)
|
|
{
|
|
int n = 0;
|
|
for ( auto& i: t )
|
|
{
|
|
if ( i.size() > 3 )
|
|
n+=2;
|
|
else if ( i.size() == 2 )
|
|
n++;
|
|
}
|
|
if ( n == 1 )
|
|
return 0;
|
|
|
|
int m = numeric_limits<int>::max();
|
|
for ( int i = 0; i < t.size(); i++)
|
|
{
|
|
if ( t[i].size() == 1 )
|
|
{
|
|
ss t2 = t;
|
|
|
|
int v = t2[i][0];
|
|
for ( int j = 0; j < t2[v].size(); ++j )
|
|
if ( t2[v][j] == i )
|
|
t2[v].erase(t2[v].begin()+j);
|
|
t2[i].clear();
|
|
|
|
int m2 = check2(t2) +1;
|
|
if ( m > m2 )
|
|
m = m2;
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
|
|
|
|
int solve_puzzle()
|
|
{
|
|
int n;
|
|
cin >> n;
|
|
ss t(n+1);
|
|
vec s;
|
|
for (int i = 0; i < n-1; i++ )
|
|
{
|
|
int v1, v2;
|
|
cin >> v1 >> v2;
|
|
s.push_back( make_pair(v1,v2) );
|
|
if ( t.size() < v1 )
|
|
t.resize(v1);
|
|
if ( t.size() < v2 )
|
|
t.resize(v2);
|
|
t[v1].push_back(v2);
|
|
t[v2].push_back(v1);
|
|
}
|
|
|
|
if ( n == 2 )
|
|
return 1;
|
|
else if (n ==1 )
|
|
return 0;
|
|
|
|
set<int> v;
|
|
for ( int i = 0; i<t.size(); ++i )
|
|
if ( t[i].size() == 2 || t[i].size() > 3 )
|
|
v.insert(i);
|
|
|
|
if ( !v.size() )
|
|
return 1;
|
|
|
|
int m = 0;
|
|
for ( auto i : v )
|
|
{
|
|
int mt = max_tree( t, i, 0 );
|
|
m = max(m,mt);
|
|
}
|
|
|
|
// if ( check2(t) != (n-m) )
|
|
// std::cout << "error" << std::endl;
|
|
|
|
return n-m;
|
|
}
|
|
|
|
int main ( void )
|
|
{try{
|
|
int puzzle_count;
|
|
|
|
std::cin >> puzzle_count;
|
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
|
|
for ( int i = 1; i <= puzzle_count; i++ )
|
|
{
|
|
std::cout << "Case #" << i << ": ";
|
|
auto r = solve_puzzle();
|
|
std::cout << r << std::endl;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
catch ( const std::exception& e )
|
|
{
|
|
std::cerr << std::endl
|
|
<< "std::exception(\"" << e.what() << "\")." << std::endl;
|
|
return 2;
|
|
}
|
|
catch ( ... )
|
|
{
|
|
std::cerr << std::endl
|
|
<< "unknown exception." << std::endl;
|
|
return 1;
|
|
}}
|