UVa679
这道题刚开始我的想法就是和书上那个超时的代码,一样的,通过一个树状数组去模拟那个过程和书上分析的那样,测试数据太过庞大,虽然没有数组移动这样耗时的程序,但for(int i=0;i<I;i++)中的for最多可以有524288
,而且还有一万组,肯定会超时。所以现在重点是如何减小对I的判断量,
模拟一个过程后,我们很容易发现到底某个点的球数Number,如果是奇数,
则最后一个往左边走,且左边比右边的球多一个,如果是偶数则往右边走,
且两边的数量一样多,然后按照书上那个优化算法写就可以了
,因为2^20约等于一百万>524288,所以for循环量被大大减小了。
UVa122
这道题非常好首先怎么输入就是个难点
因为这道题出现了跨行读取,就显得非常难处理了,仅仅通过while(scanf("%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来模拟这个图像