Files
test/puzzles/google_code_jam/2014/1A-B-code_jam.cpp

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;
}}