The second week of 2022

Posted by andymelton on Sun, 06 Mar 2022 07:04:04 +0100

A.Binary Tree

meaning of the title
If the root is (a,b), the left child is (a+b,b) and the right child is (a,a+b). Given (m,n), the initial root is (1,1). From (1,1) to (m,n), you need to walk several times to the left subtree and several times to the right subtree.
Problem solving ideas
Idea 1: reverse thinking, from (m,n) to (1,1). Given (m,n), find his father. If M > N, his father is (m-n,n), otherwise (m,n-m). But this method will time out.
Idea 2: use division instead of subtraction, and the quotient obtained is the number of times to go left, and the last m=m%n. n> M, and so on. Special attention should be paid to: what if M > N, M% n = = 0? Because the root (1,1) cannot have 0, special treatment: Times: m/n-1; m=1
code

#include <iostream>
using namespace std;

int main(){
    int T, a, b, lcnt, rcnt;
    cin >> T;
    for(int i = 1; i <= T; ++i) {
        cin >> a >> b;
        lcnt = rcnt = 0;
        while(a != 1 || b != 1) {
            if(a >= b) {
                if(a % b) {
                    lcnt += a/b; //Optimize subtraction, one division operation to avoid repeated subtraction
                    a %= b;
                }
                else{
                    lcnt += a/b-1;
                    a = 1;
                }
            }
            else{
                if(b % a) {
                    rcnt += b/a; 
                     b %= a;
                }
                else{
                    rcnt += b/a-1;
                    b = 1;
                }
            }
        }
        cout << "Scenario #" << i << ':' << endl;
        cout << lcnt << ' ' << rcnt << endl <<endl;
    }
    return 0;
}

B.Falling leaves

meaning of the title
Given a binary search tree and a series of related operations:
(1) Delete the leaf node and give the deleted data;
(2) Repeat this process until the binary tree is empty.
Start the above operation from the leaf node in the lower left corner to obtain a series of node sequences of the binary tree. According to the given tree node sequence of these lines (the interval between trees is "*", and the input end flag is "$"), find the node sequence traversed by the binary tree in advance.
Problem solving ideas
According to the given node sequence of several lines, the tree is built first, and then the first order traversal is carried out.
code

#include<cstdio>
#include<cstring>
using namespace std;
 
struct node{
    char c;
    int lch, rch;
}tree[30];
char s[30][30];
int e;
void add(int n, char c)
{
    if(tree[n].c > c) {
        if(!tree[n].lch) {
            tree[e].c = c;
            tree[n].lch = e++;
        }
        else add(tree[n].lch, c);
    }
    else {
        if(!tree[n].rch) {
            tree[e].c = c;
            tree[n].rch = e++;
        }
        else add(tree[n].rch, c);
    }
}
 
void pre_order(int n)
{
    printf("%c",tree[n].c);
    if(tree[n].lch) pre_order(tree[n].lch);
    if(tree[n].rch) pre_order(tree[n].rch);
}
int main()
{
    int cnt = 0;
    while(~scanf("%s",s[cnt++])) {
        if(s[cnt-1][0] == '*' || s[cnt-1][0] == '$') {
            e = 0;
            memset(tree,0,sizeof(tree));
            int n = cnt-1;
            tree[e++].c = s[n-1][0];
            for(int i = n-2; i >= 0; i--) {
                int len = strlen(s[i]);
                for(int j = 0; j < len; j++)
                    add(0,s[i][j]);
            }
            pre_order(0);
            printf("\n");
            if(s[cnt-1][0] == '$') break;
            cnt = 0;
        }
    }
    return 0;
}

C.Tree Recovery

meaning of the title
The general meaning of this question is to give you the pre order traversal sequence of a binary tree and the middle order traversal sequence of a tree. Now let you find the post order traversal sequence of this tree.
Problem solving ideas
This problem is relatively simple, that is, the idea of recursion. The first node of the pre order traversal sequence is the root. The root node will divide the middle order traversal sequence into two parts (some parts may be empty). The left is the middle order traversal node sequence of the left subtree, and the right is the middle order traversal node sequence of the right subtree. In this way, the number of nodes of the left subtree and the right subtree is determined, According to the number of nodes of the left and right subtrees, we can get the sequence of pre traversal nodes of the left and right subtrees, and solve the problem recursively.
code

#include <iostream>
using namespace std;
#define N 27
char pre[N],in[N];//Pre represents the pre order traversal sequence, and in represents the middle order traversal sequence 
int id[N];
void print(int a,int b,int c,int d)//a. B, C and d represent the starting point and end point of the preorder and middle order traversal sequences respectively 
{
    int i=id[pre[a]-'A'];//Root node 
    int j=i-c;//Left subtree of middle order traversal sequence 
    int k=d-i;//Right subtree of middle order traversal sequence 
    if(j)   print(a+1,a+j,c,i-1);//If the left subtree is not empty, the left subtree is recursive 
    if(k)   print(a+j+1,b,i+1,d);//If the right subtree is not empty, the right subtree is recursive 
    printf("%c",pre[a]);
}
int main()
{
    while(~scanf("%s%s",pre,in))
    {
        for(int i=0;in[i];i++)  
            id[in[i]-'A']=i; 
        int len=strlen(in);
        print(0,len-1,0,len-1);
        puts("");
    }
    return 0;
}

D.Entropy

meaning of the title
This problem is to find the weighted path length of Huffman tree, and then output the space occupied by the original string (8 bits per character), the space occupied after compression (weighted path length - when the frequency is directly expressed by the number of occurrences), and the ratio of the two values.
The so-called weighted path length of the tree is the weight of all leaf nodes in the tree multiplied by the path length to the root node (if the root node is layer 0, the path length from leaf node to root node is the number of layers of leaf node).
Problem solving ideas
Take a chestnut: abbcc
The weights are 1, 2 and 3 respectively.
First, A and B are used to generate A tree. At this time, the corresponding code of A is 0, B is 1, abb is 011, which is A three bit length. Then, when this tree is combined with C, the code length of A and B is increased by 1. At this time, A is 00, B is 01, and the length of ABB code length increase is 1 + 2 (that is, the weight of the tree combined for the first time).
Therefore, with this idea, you can directly use the priority queue to store the weights without building a Huffman tree, add the two smallest weights (a+b) to sum each time, and then press (a+b) into the queue.
code

#include<queue>
#include<functional>
#include<algorithm>
#include<vector>
#include<iostream>
#include<string> 
 
using namespace std;
 
template <typename T> void print_queue(T& q){
    while(!q.empty()){
        std::cout<<q.top()<<" ";
        q.pop();
    }
    std::cout<<std::endl;
}
 
int main(){
    string s;
    while(getline(cin,s) && s!="END"){
        //First, sort the strings in dictionary order and count the number of occurrences of the same character
        std::sort(s.begin(),s.end());
        // cout<<s<<endl;
        priority_queue<int,vector<int>,greater<int>>q;//Take greater as the parameter of Compare to ensure that the elements obtained through the top() operation of the priority queue are the smallest elements of the queue
        int cnt = 1;//cnt is used to count the number of occurrences of scanned characters
        for( int i = 0; i < s.length();++i){
            if(s[i]!=s[i+1]){       
                q.push(cnt);        //If the current character is not the same as the previous character, it indicates that the count of times of the previous character has been completed, and the occurrence times of this character can be put into the priority queue
                cnt = 1;            //And reset the variable for counting the number of characters to indicate the number of times a new character appears
            }else{
                ++cnt;              //The current character is compared with the previous character. If it is the same character, it will be used to count the number of times of the current character plus one
            }
        }
        // print_queue(q);
        int leng = 0;
        //Dealing with degradation, there is only one character input
        if(q.size()==1)
        leng = q.top();
 
        while(q.size()!=1){         //Note that after the Huffman tree is merged, there is only one element in the priority queue, and this element is the root node of the Huffman tree
            int min_1 = q.top();
            q.pop();                //It can be understood as taking out the two trees with the least weight from Huffman forest
            int min_2 = q.top();
            q.pop();
            q.push(min_1+min_2);    //Merge the two Huffman trees with the smallest weight into a tree whose weight is the sum of the weights of the two trees, and add the tree to the Huffman forest
            leng += (min_1+min_2);  //The method of calculating the Huffman encoding length of the whole string is really not so easy to understand
            //The length of a character in the Huffman coding length is the number of times it appears and the length of its path in the Huffman tree
            //Suppose that the frequency of this character is w, and its path length in this Huffman tree is h
            //In the process of constructing this Huffman coding tree, this character node (can be used as the root node of a Huffman tree alone or as the leaf node of the merged Huffman tree)
            //Take out from the priority queue for h times, merge and then add to the priority queue
            //Statement "leng += (min_1+min_2);"(min_1+min_2) must contain the frequency w of this character and execute it h times
            //Analyzing other characters by this method meets this law
            //Thus, in the process of merging Huffman trees, the length of Huffman encoded string is calculated       
        }
        // cout<<leng<<endl;
        // cout<<8.0*s.length()/leng<<endl;
        printf("%d %d %.1f\n",8*s.length(),leng,8.0*s.length()/leng);
    }
}

E.BST

meaning of the title
For a full binary tree, all nodes are numbered from 1 in order from left to right to become a binary search tree. Now, given a node number each time, ask the minimum number and maximum number nodes of the subtree with this node as the root.
Problem solving ideas
This question examines the basic properties of the full binary tree. It can be seen from the observation that the number of layers K of a number x is equal to the position of the rightmost 1 of the binary of this number. For example, 12, its binary is 1100, and the position of the rightmost 1 is 3, so the number of layers of 12 is k = 3, which can be obtained from the properties of the full binary tree: the number of nodes of the tree is equal to 2^k - 1, Therefore, the node digits of its left and right subtrees (2^k - 1 - 1) / 2= 2^(k - 1) - 1, and because 2^(k - 1) = lowbit(x) The node range of the left subtree is [min,x-1], and the range of the right subtree is [x+1,max], so min = x - lowbit(x) + 1,max = x + lowbit(x) +1
Note: the lowbit() function is used to take a number composed of the lowest binary one and the following zero
code

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
 
int main(){
    int n;
    scanf("%d",&n);
    while(n--){
        int x;
        scanf("%d",&x);
        int t = x & (-x);//The negative number of binary is a positive number, and the negative number is plus one.
						//A number and its negative number are "and" to realize the lowbit() function
        printf("%d %d\n",x-t+1,x+t-1);
    }
    return 0;
}

F. Food chain

meaning of the title
Find the number of lies.
Problem solving ideas
Maintain the three relationships of each animal: what is similar to itself, what eats itself, and what is eaten by itself. If three arrays are used for maintenance, that is, A represents the same kind, B represents what is eaten by oneself, and C represents what is eaten by oneself, then the same pre array cannot be used for maintenance, because the meanings of A[i], B[i], and C[i] are different, but pre[i] has only one meaning. What shall I do? In fact, it's very simple. In order to solve the repetition problem of pre, we can open A one-dimensional array three times as large as n. Where 1N represents the same kind, N+1N+N represents being eaten by the animals corresponding to 1N, and 2N+12N+N represents eating the animals corresponding to 1~N. in this way, we can use A pre array to maintain the relationship between the three.
It should also be noted that there is only one set of data input, otherwise it will be WA.
code

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100010
using namespace std;
 
int pre[maxn*3];
int N,K;
 
int find(int x){
    int t=x;
    while(pre[t]!=t) t=pre[t]; 
    while(x!=t) pre[x]=t, x=pre[x]; 
    return t;
}
 
int judge(int a,int b,int c){
    if(a==2 && b==c) {return 0;} //If b and c are the relationship between b and c, and b and c are the same species, it must be a lie
    if(b>N || c>N) {return 0;} //If b and c exceed the specified maximum species range, it can also be judged to be false.
    int fb=find(b),fc=find(c);
    if(a==1){//When X and Y are of the same kind
        if(fb==find(c+N) || fb==find(c+2*N)){ return 0;}//If b is eaten or eaten by c, it is also a lie
		 //The next three steps are to make b and c the same kind in the array
        pre[fb]=fc;
        pre[find(b+N)]=find(c+N);  
        pre[find(b+2*N)]=find(c+2*N);
        return 1;
    }
if(fb==fc || fb==find(c+N)) {return 0;}//If b and c are the same species as another song, it means that b and c are the same species. At this time, if b eats c, it is a lie
//The next three steps are to change the relationship between B and c into b eating c in the array;
    pre[fb]=find(c+2*N);
    pre[find(b+N)]=fc;
    pre[find(b+2*N)]=find(c+N);
    return 1;
}
 
int main(){
    cin>>N>>K;
    int a,b,c,ans=0;
    for(int i=0;i<=3*N;i++) pre[i]=i;//Initialize pre [] array
    for(int i=0;i<K;i++){
        scanf("%d %d %d",&a,&b,&c);
        if(judge(a,b,c)) continue;
        ans++;
    }
    cout<<ans<<endl;
    return 0;
}

G.Fence Repair

meaning of the title
The cost of sawing a certain length of wood is equal to the current cost of sawing a certain length of wood.
Problem solving ideas
The length of the wood sawn each time is the sum of the length of the wood sawn next time. This is obviously the operation of Huffman tree construction, but the data given is disordered. We have to find the smallest two elements every time. This process is a waste of time, so we can't simply use the array to store the input data.
In this topic, we can use priority queue to realize Huffman tree, and priority queue can use priority in STL container_ Queue, you can also use the idea of heap to create a priority queue.
code

#include<stdio.h>
#include<algorithm>
#include<queue>
using namespace std; 
typedef long long l1;
int n,L[20001];
//The idea of priority queue is used here
priority_queue<int, vector<int>,greater<int> > que; //The priority queue was originally taken from large to small, but here it is changed from small to large
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++){
            scanf("%d",&L[i]);
            que.push(L[i]);
        }
        l1 ans=0;
        while(que.size()>1){//You can't use que here Empty() judgment
            int num1=que.top();
            que.pop();
            int num2=que.top();
            que.pop();
            que.push(num1+num2);
            ans+=(num1+num2);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

H.Color a Tree

meaning of the title
Given a tree, including the number of nodes in the tree, the weight of each node, and the condition of each edge. Now color the tree. The requirements are as follows:
1) If you paint a tree, its parent node must have been painted, and the tree root must be painted first;
2) Color painting starts from time 1. The next node can only be painted after each node is painted, and the painting time of each node is 1 unit.
Now it is stipulated that the total weight of the whole coloring process is the sum of the coloring time points of all nodes and their weight products.
Problem solving ideas
First, we know that because of condition 1), the coloring order must be a topological sequence. If there is no limit of 1), then the meaning of the question becomes that there are n vertices. The weight of each vertex is known. Now we need to paint and minimize the total weight. Then the greedy strategy is obvious, that is, the one with the largest weight is put in front. Because of the limitation of condition 1), the coloring order must abide by certain rules, that is, topological sequence. Here we can guess that if the parent node must color before the son node, the son node with the largest weight should not color immediately after the parent node, so that it can be close to the optimal situation without condition 1).
So we get the first conjecture: the node with the largest weight must be painted immediately after its parent node is painted.
Here is the proof: let S represent the node with the largest weight, and its parent node is recorded as P. suppose that after painting P, paint S first, and then paint other K nodes, that is, the time sequence is: PSn1... nK, and the time spent is:

F1= T×Cp + (T+1)×Cs + {sigma[(T+1+i)×Cni],i=1->k}

If K nodes are painted after S, that is, the time sequence is Pn1n2... nkS, and the time spent is:

F2= T×Cp + {sigma[(T+i)×Cni],i=1->k} + (T+k+1)×Cs

F1-F2= {sigma(Cni),i=1->k} - k × Cs

Because S is the non root node with the largest weight in the tree,
So CNI < = CS, {sigma (CNI), I = 1 - > k} < = k × Cs,
So F1-F2 < = 0
The weight of k nodes n1,n2,..., nk will not be greater than Cs, because the non root node with the largest weight in the tree is s, and the weight of the root node may be greater than Cs. However, due to the existence of p, p may be the root node, and n1,n2,..., nk cannot be the root node.
Therefore, it is proved that the non root node with the largest weight in the tree is p, and the parent node of P is q. after q coloring, it is p coloring at the next time point, which can minimize the total cost.
In this way, we can merge the non root node p with the largest weight and its parent node q into one node. The weight of the merged new node is (Cp+Cq)/2, the parent node is the parent node of q, the son node is the son node of q and the son node of p.

The certificate is as follows:
Suppose there are two choices: one is to dye q and p, and then dye k nodes of non-q offspring;
The second is to stain k nodes (n1,n2,..., nk) of non-q offspring, and then stain q and p.
The difference between the cost of the second option and the cost of the first option is:
F2-F1=(Cq+Cp)×k-{sigma(Cni),i=1->k}×2
In other words, the second scheme dyes k nodes first, which is 2 time points earlier than the first scheme,
Then the cost saved is 2 × {sigma(Cni),i=1->k}; After q and p staining,
Compared with the first scheme, it is delayed by K time points, and the increased cost is (Cq+Cp) × k.
Which option will get the best result?
(F2-F1)/(2×k) = (Cq+Cp)/2 - {sigma(Cni),i=1->k}/k
If q and p are merged into one node x, Cx=(Cp+Cq)/2, n1,..., nk are merged into one node y,
CY = {sigma (CNI), I = 1 - > k} / K, then the problem we are facing is whether to dye x or y first.
Obviously, dyeing the points with large weight first and then dyeing the points with small weight will get the optimal solution.
The average of the weights of the nodes is used as the arithmetic value of the new node.
According to the proof conclusion of conclusion 2, when merging each time, by calculating all of the original tree contained in the two nodes
The arithmetic mean of the weights of the nodes is used as the weights of the new overall nodes.
code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 1010
struct Node{ // Adjacency table
		int index; // Node serial number
		struct Node *next; // Point to the next child node
	}node[Max]; 
	int num[Max]; //Record the number of nodes, including the number of nodes after merging
	int pre[Max]; //Record node parent node
	int value[Max]; //Record node weights
	bool flag[Max]; //Whether the tag has been merged
	int n,r; // Number of nodes and root node number
	int Sum; // Minimum total weight
	void add(int a,int b){ //Construct the adjacency table, which records the son nodes of node a
		struct Node *temp=(struct Node*)malloc(sizeof(struct Node));
		temp->index=b;
		temp->next=node[a].next;
		node[a].next=temp;
	}
	int find(){ //Find the minimum value label of value[i]/num[i]. Note that it cannot be the root node
		double tt=-1.0;
		int index;
		for(int i=1;i<=n;i++)
		if(!flag[i] && double(value[i])/num[i]>tt && i!=r){
				tt=double(value[i])/num[i];
				index=i;
			}
		return index;
	}
	void merge(int pra,int son){ // Merge the parent node and child node to become a new node, and update the parent node of the child node and child node at the same time
		num[pra]+=num[son]; //Number of nodes after consolidation
		value[pra]+=value[son]; //Combined node weights
		struct Node *point=node[son].next;
		while(point!=NULL){ //Update child node, child node, parent node
			pre[point->index]=pra;
			point=point->next;
		}
	}
	void cal(){ // Greedy algorithm for minimum total weight
		for(int i=1;i<n;i++){ //Need to merge n-1 times
			int index=find(); // Find max value[i]/num[i]
			flag[index]=true; //Tags have been merged
			int pra=pre[index]; //Find its parent node
			while(flag[pra]) //Until there is no merger
				pra=pre[pra];
			Sum+=(value[index]*num[pra]); //Add the combined weight
			merge(pra,index);//Merge parent and child nodes
		}
		Sum+=value[r]; //Finally, since the weights of all nodes except the root node are actually multiplied by (actual path - 1), it is also necessary to add the sum of ownership values and the weights of the root node. At this time, the value is exactly the sum of the initial weights of all nodes + the weights of the root node, so it is necessary to add here
}
	int main(){
		while(scanf("%d%d",&n,&r),n|r){
			for(int i=1;i<=n;i++){
				scanf("%d",&value[i]); //Input node weight
				num[i]=1; //The initial number is 1
				node[i].next=NULL; // Initialize to NULL and establish adjacency table by header interpolation
			}
			int aa,bb;
			memset(flag,0,sizeof(flag)); //Initialize as not merged
			for(int i=1;i<n;i++){ //Enter edge information
				scanf("%d%d",&aa,&bb);
				add(aa,bb); //Establish adjacency table
				pre[bb]=aa;
			}
			Sum=0; //Initialize to 0
			cal(); //Greedy calculation of the lowest total weight
			printf("%d\n",Sum);//output
		}
		return 0;
}

Topics: C++ Algorithm Graph Theory