《算法竞赛入门经典》第六章6.3树和二叉树总结

UVa679

这道题刚开始我的想法就是和书上那个超时的代码,一样的,通过一个树状数组去模拟那个过程和书上分析的那样,测试数据太过庞大,虽然没有数组移动这样耗时的程序,但for(int i=0;i<I;i++)中的for最多可以有524288
,而且还有一万组,肯定会超时。所以现在重点是如何减小对I的判断量,
模拟一个过程后,我们很容易发现到底某个点的球数Number,如果是奇数,
则最后一个往左边走,且左边比右边的球多一个,如果是偶数则往右边走,
且两边的数量一样多,然后按照书上那个优化算法写就可以了
,因为2^20约等于一百万>524288,所以for循环量被大大减小了。

UVa122

这道题非常好首先怎么输入就是个难点
因为这道题出现了跨行读取,就显得非常难处理了,仅仅通过whilescanf("%s",str)!=EOF这样没有办法判断每两次之间的分割符”()“,刘汝佳很巧妙的通过写一个read_input()函数来解决这个问题,因为while循环每次都要判断,所以读到()就返回true这样while还会继续,读到EOF返回false,程序结束。

正如刘汝嘉所说,大部分的ACM题并不需要内存池,但是学习这个可以锻炼工程思维,那段程序大致的作用是,首先声明一个足够大的数组,数组元素是Node,然后把他们的指针一一放到,一个队列中,每次需要新的节点时就从队首弹出来一个,如何回收那些用过的内存内,将remove_tree中的delete u;换成deletenode(u),就是把那些内存重新放入队尾

这道题需要判断什么样的二叉树是不正确的,有两种错误情况
1.有节点没有被赋值
2.有节点被重复赋值
设置一个全局变量failed都判断一下就行了

注意记住几个小技巧,strchr sscanf;

使用数组代替指针的方法与6.2最后一题思想类似。和结构体的区别就是他用四个数组来分别存放四个变量。
其实have_value这个变量不是必需的,只是有程序更健壮,用v这个值就可以判断了(再初期先全部赋值为0)

附上数组代码,其他两种代码仓库有
#pragma warning(disable:4996)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<queue>
#include<time.h>

using namespace std;

const int maxn = 256 + 10;


bool failed;
int cnt;
const int root = 1;
int left[maxn];
int right[maxn];
bool have_value[maxn];
int value[maxn];


void newtree()
{
    left[root] = right[root] = 0;
    have_value[root] = false;
    cnt = root;
}

int newnode()
{
    int u = ++cnt;
    left[u] = right[u] = 0;
    have_value[u] = false;
    return u;
}

void addnode(int v, char* s) {
    int n = strlen(s);
    int u = root;
    for (int i = 0; i < n; i++)
        if (s[i] == 'L') {
            if (left[u] == 0)left[u] = newnode();
            u = left[u];
        }
        else if (s[i] == 'R') {
            if (right[u] ==0) right[u] = newnode();
            u = right[u];
        }
        if (have_value[u]) failed = true;
        value[u] = v;
        have_value[u] = true;
}

char s[maxn];

bool read_input() {
    memset(value, 0, sizeof(value));
    memset(left, 0, sizeof(left));
    memset(right, 0, sizeof(right));
    memset(have_value, false, sizeof(have_value));
    failed = false;
    newtree();
    for (;;) {
        if (scanf("%s", s) != 1) return false;
        if (!strcmp(s, "()")) break;
        int v;
        sscanf(&s[1], "%d", &v);
        addnode(v, strchr(s, ',') + 1);
    }
    return true;
}

bool bfs(vector<int>& ans) {
    queue<int> q;
    ans.clear();
    q.push(root);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        if (!have_value[u]) return false;
        ans.push_back(value[u]);
        if (left[u] != 0) q.push(left[u]);
        if (right[u] != 0) q.push(right[u]);
    }
    return true;
}

int vs_main() {
    vector<int> ans;
    while (read_input()) {
        if (!bfs(ans)) failed = true;
        if (failed) printf("not complete\n");
        else {
            for (int i = 0; i < ans.size(); i++) {
                if (i != 0) printf(" ");
                printf("%d", ans[i]);
            }
            printf("\n");
        }
    }
    return 0;
}

int main()
{
    int start = clock();
    freopen("in.txt", "r", stdin);
    //freopen("F:\\out.txt","w",stdout);
    //printf("#===================#\n");
    vs_main();
    //printf("#===================#\n");
    //printf("Time:%.3lf\n", double(clock() - start) / CLOCKS_PER_SEC);
    system("pause");
    return 0;
}

UVa548

这题的思想是后续的最后一个节点是跟节点,然后根据这个节点在中序遍历中的位置,
来讲左右子树划分出来,然后根据中序划分的子树中各节点的数量,
来找出对应后序中的序列,递归下去,直到最后序列为空(即空树的时候)
然后通过深度搜索从根开始搜到所有叶子节点找出最优解

良好习惯数组命名
先序pre_order
中序in_order
后序post_order

在递归的同时统计最优解还没写,晚上补,
写完了,可以附代码

#pragma warning(disable:4996)
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<algorithm>
#include<iostream>
#include<time.h>
#include<set>
#include<sstream>
#include<functional>
#include<cassert>
#include<list>
#include<iterator>
#include<utility>
#include <stdexcept>
#include <sstream>
#include <fstream>
#include<ctype.h>
#include<map>
#include<stack>
#include<queue>
#include<cstdlib>

using namespace std;
// 因为各个结点的权值各不相同且都是正整数,直接用权值作为结点编号
const int maxv = 10000 + 10;
int in_order[maxv], post_order[maxv], lch[maxv], rch[maxv];
int n;

bool read_list(int* a) {
    string line;
    if (!getline(cin, line)) return false;
    stringstream ss(line);
    n = 0;
    int x;
    while (ss >> x) a[n++] = x;
    return n > 0;
}

int best, best_sum; // 目前为止的最优解和对应的权和
// 把in_order[L1..R1]和post_order[L2..R2]建成一棵二叉树,返回树根
int build(int L1, int R1, int L2, int R2,int sum) {
    if (L1 > R1) return 0; // 空树
    int root = post_order[R2];
    int p = L1;
    while (in_order[p] != root) p++;
    sum += root;
    int cnt = p - L1; // 左子树的结点个数
    lch[root] = build(L1, p - 1, L2, L2 + cnt - 1,sum);
    rch[root] = build(p + 1, R1, L2 + cnt, R2 - 1,sum);
    if (L1 == R1)
    {
        if (sum < best_sum || sum == best_sum&&root < best)
        {
            best_sum = sum;
            best = root;
        }
    }
    return root;
}



int vs_main() {
    while (read_list(in_order)) {
        read_list(post_order);
        int sum = 0;
        best_sum = 1000000000;
        best = 100000000;
        build(0, n - 1, 0, n - 1,0);
        cout << best << "\n";
    }
    return 0;
}
int main()
{
    int start = clock();
    freopen("in.txt", "r", stdin);
    //freopen("F:\\out.txt","w",stdout);
    //printf("#===================#\n");
    vs_main();
    //printf("#===================#\n");
    //printf("Time:%.3lf\n", double(clock() - start) / CLOCKS_PER_SEC);
    system("pause");
    return 0;
}


思想应该是每次都加上该节点的权值,因为需要求sum,递归的过程正好就是从根出发的。

UVa839

以后做题要注意观察,发现其实这种天平和二叉树的形式是非常类似的,
题目的输入方法就是树的先序遍历,递归找到W不为零的部分,然后通过&W来返回总的重量

UVa699

因为给出了-1,所以我认为可以通过先序就能建树,晚上试下,
//可以,,但是蜜汁RE没有发现原因样例和udebug都过了
#pragma warning(disable:4996)
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<algorithm>
#include<iostream>
#include<time.h>
#include<set>
#include<sstream>
#include<functional>
#include<cassert>
#include<list>
#include<iterator>
#include<utility>
#include <stdexcept>
#include <sstream>
#include <fstream>
#include<ctype.h>
#include<map>
#include<stack>
#include<queue>
#include<cstdlib>

using namespace std;

const int maxn = 2000;
int value[maxn];
struct Node
{
    int v;
    Node* left, *right;
    Node() :left(NULL), right(NULL), v(0) {};
};
Node* root;

Node* newnode()
{
    return new Node();
}

bool input(Node* root)
{
    int temp;
    cin >> temp;
    root->v = temp;
    if (temp == -1)
        return false;
    root->left = newnode();
    root->right = newnode();
    input(root->left);
    input(root->right);
    return true;
}

int pos = 1000;
int cnt = 0;
int flag;
void dfs(int flag,Node* root)
{
    if (root->v == -1)
    {
        if (flag == -1)
            cnt++;
        if (flag == 1)
            cnt--;
        return;
    }
    value[cnt + pos] += root->v;
    cnt--;
    dfs(-1,root->left);
    cnt++;
    dfs(1,root->right);
    return (void)(flag == 1 ? cnt-- : cnt++);
}


int vs_main()
{
    int cas = 0;
    root=newnode();
    while (memset(value,0,sizeof(value)),input(root))
    {
        dfs(flag,root);
        cout << "Case " << ++cas <<":"<< endl;
        for (int i = 0;i <2000;i++)
        {
            if (value[i] != 0)
                cout << value[i] << " ";
        }
        cout << endl << endl;
    }
    return 0;
}

int main()
{
    int start = clock();
    freopen("in.txt", "r", stdin);
    //freopen("F:\\out.txt","w",stdout);
    //printf("#===================#\n");
    vs_main();
    //printf("#===================#\n");
    //printf("Time:%.3lf\n", double(clock() - start) / CLOCKS_PER_SEC);
    system("pause");
    return 0;
}

刘汝嘉的思想是先选取一个数组中间的位置(保证足够大,即左右两边都不会越界)
然后递归建树(虚拟建树,统计各距离的sum值),
最后从数组第一个不为零的位置开始输出所有不为零的值计科

UVa297

四分树用途及其广阔,找个时间专门坐下
四分树因为有中心节点,所以仅仅一种遍历顺序就可以建树了
每次碰到p划分一次,边长减一半,因为是求两次之和所以直接用一个二维buf来模拟这个图像

猜你喜欢

转载自blog.csdn.net/jack_jyh/article/details/53324372