PAT-Advanced Level-第七章到第九章

7.1 栈的应用

A1051

思路:按照题目给定序列模拟,将1-n依次入栈,在入栈过程中如果栈顶元素等于出栈序列的元素,则出栈,此时只要栈顶元素等于出栈序列元素,则一直出栈。
注意两个问题:1.每个序列入栈之前将栈清空!
2.每次pop之前必须判断栈是否空,否则可能出现段错误!!!!
3.判断的逻辑是:每次push一个i入栈,然后判断栈大小是否大于M,若是则令flag=false,同时break, 然后进入循环判断栈顶元素和出栈序列是否相同,相同则一直出栈,current一直++,直到栈空或者不相等为止。

以下为书中代码:

#include <cstdio>
#include <stack>
using namespace std;
const int maxn=1010;
int arr[maxn];
stack<int> st;
int main() {
	int m, n, k;
	scanf("%d%d%d", &m, &n, &k);
	while(k--) {
		while(!st.empty()) {		//每次循环前清空栈!!!
			st.pop();
		}
		for(int i=1; i<=n; i++) {
			scanf("%d", &arr[i]);
		}
		int current=1;
		bool flag=true;
		for(int i=1; i<=n; i++) {
			st.push(i);
			if(st.size()>m) {
				flag=false;
				break;
			}
			while(!st.empty() && st.top()==arr[current]) {
				st.pop();
				current++;
			}
		}
		if(st.empty()==true && flag==true) {
			printf("YES\n");
		} else {
			printf("NO\n");
		}
		
	}
	return 0;
}

A 1056

问题总结:1.刚开始不知道这个排序是怎么得到的,读了很多遍题目也不懂,看了书才理解了,是倒着排序的,且同一轮的老鼠排序一致,且占位置,即如果排序为2的老鼠有两只,那么比它们低一级的老鼠排序为4!!!!!!!!!!
2.想不明白这道题咋用queue,=.=我是不是很蠢,每次都遍历temp只老鼠,初始时定义temp=np,然后分成group组,每组寻找最大值,注意在赋rank后要及时pop,质量最大的序号以k记忆,在每次一个group遍历完后,再将质量最大的老鼠序号push到队列的尾部,注意,由于还在group各组的循环内,所以temp是不会变的,也就是说,这group次循环刚好遍历temp个老鼠,下一次循环的时候定义temp为group即可!
以下为书中代码:

#include <cstdio>
#include <queue>
using namespace std;
const int maxn=1010;
struct Mouse{
	int weight;
	int R;
}mouse[maxn];

int main() {
	int np, ng, order;
	scanf("%d%d", &np, &ng);
	for(int i=0; i<np; i++) {
		scanf("%d", &mouse[i].weight);
	}
	queue<int> q;
	for(int i=0; i<np; i++) {
		scanf("%d", &order);
		q.push(order);
	}
	int temp=np, group;
	while(q.size()!=1) {
		if(temp%ng==0) group=temp/ng;
		else group=temp/ng+1;
		for(int i=0; i<group; i++) {	//遍历temp只老鼠 
			int k=q.front();
			for(int j=0; j<ng; j++) {	//寻找最大重量的老鼠 
				if(i*ng+j>=temp) break;	//超出范围
				int front=q.front();
				if(mouse[front].weight>mouse[k].weight){
					k=front;
				} 
				mouse[front].R=group+1;
				q.pop();
			}
			q.push(k);
		}
		temp=group;
	}
	mouse[q.front()].R=1;
	for(int i=0; i<np; i++) {
		printf("%d", mouse[i].R);
		if(i<np-1) printf(" ");
	}
	return 0;
}

A 1074

思路:1.想到了用静态链表做,但是一直在考虑如何将链表反转,实际上只需要反向打印输出就可以
2.一定要找到规律,发现很多算法题关键在于如何找到规律,其实也不难,分几种情况讨论好,然后一个个写就可以
3.对于静态链表的序号的关系理解还比较模糊,静态链表的序号表示其地址,结点的next域表示其后续的结点的地址(序号),因此当我们按照题目给定的地址排好序之后,并给定了order,那么这样就可以直接按照下标输出了,就不用再考虑怎么把next域串起来,因为我们已经排好序了,本质上成为了一个顺序表!
以下为书中代码:

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100010;
struct Node{
	int address, data, next;
	int order;
}node[maxn];
bool cmp(Node a, Node b) {
	return a.order<b.order;
}

int main() {
	for(int i=0; i<maxn; i++) {
		node[i].order=maxn;
	}
	int first, n, k, temp;
	scanf("%d%d%d", &first, &n, &k);
	for(int i=0; i<n; i++) {
		scanf("%d", &temp);
		scanf("%d%d", &node[temp].data, &node[temp].next);
		node[temp].address=temp;
	}
	int p=first, count=0;
	while(p!=-1) {
		node[p].order=count++;
		p=node[p].next;
	}
	n=count;
	sort(node, node+maxn, cmp);
	for(int i=0; i<n/k; i++) {
		for(int j=(i+1)*k-1; j>i*k; j--) {
			printf("%05d %d %05d\n", node[j].address, node[j].data, node[j-1].address);
		}
		printf("%05d %d ", node[i*k].address, node[i*k].data);
		if(i<n/k-1) printf("%05d\n", node[(i+2)*k-1].address);
		else {
			if(n%k==0) {
				printf("-1\n");
			} else {
				printf("%05d\n", node[(i+1)*k].address);
				for(int i=n/k*k; i<n; i++) {
					printf("%05d %d ", node[i].address, node[i].data);
					if(i<n-1) {
						printf("%05d\n", node[i+1].address);
					} else {
						printf("-1\n");
					}
				}
			}
		}
	}
	return 0;
}

A 1032

问题总结:1.对于链表的遍历还不太熟悉,同时这道题的简单解法没有想到,只需要找到这两个单词有没有公共结点就可以了,而找公共结点通过一个flag域即可确定!
以下为书中代码:

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100010;
struct Node{
	int next;
	char data;
	bool flag;
}node[maxn];

int main() {
	for(int i=0; i<maxn; i++) {
		node[i].flag=false;
	}
	int first1, first2, n, address, next;
	char data;
	scanf("%d%d%d", &first1, &first2, &n);
	for(int i=0; i<n; i++) {
		scanf("%d %c %d", &address, &data, &next);
		node[address].data=data;
		node[address].next=next;
	}
	int p;
	for(p=first1; p!=-1; p=node[p].next) {
		node[p].flag=true;
	}
	for(p=first2; p!=-1; p=node[p].next) {
		if(node[p].flag==true) {
			break;
		}
	}
	if(p!=-1) {
		printf("%05d\n", p);
	} else {
		printf("-1\n");
		return 0;
	}
} 

A 1052

问题总结:1.这道题自己用暴力的做法发现大部分测试点都可以通过,还以为会超时呢,最后一个测试点无法通过,显示答案错误,不应该啊,应该没问题啊,不知道什么边界条件未考虑!
看完书发现是有可能出现没有有效结点的情况,这时候要特判输出 0 -1

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100010;
struct Node{
	int address, data, next;
	int order;
}node[maxn];
bool  cmp1(Node a, Node b) {
	return a.order<b.order;
}
bool cmp2(Node a, Node b) {
	return a.data<b.data;
}
int main() {
	for(int i=0; i<maxn; i++) {
		node[i].order=maxn;
	}
	int n, first;
	scanf("%d%d", &n, &first);
	int address;
	for(int i=0; i<n; i++) {
		scanf("%d", &address);
		scanf("%d%d", &node[address].data, &node[address].next);
		node[address].address=address;
	}
	
	int p=first, count=0;
	while(p!=-1) {
		node[p].order=count++;
		p=node[p].next;
	}
	if(count==0) {
		printf("0 -1\n");
		return 0;
	}
	sort(node, node+maxn, cmp1);
	sort(node, node+count, cmp2);
	printf("%d %05d\n", count, node[0].address);
	for(int i=0; i<count-1; i++) {
		printf("%05d %d %05d\n", node[i].address, node[i].data, node[i+1].address);
	}
	printf("%05d %d -1\n", node[count-1].address, node[count-1].data);
	return 0;
}

以下为柳神代码:非常简洁!!!!!!=.=
主要思路及不同点:下面的代码又又又在结构体中增加了flag,然后通过flag判断是否为有效结点,之后排序的时候直接按照flag和key进行排序!!!!!!

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100010;
struct Node{
	int address, key, next;
	bool flag;
}node[maxn];
bool cmp(Node a, Node b) {
	return (!a.flag || !b.flag)? a.flag>b.flag : a.key<b.key;
}
int main() {
	int n, cnt=0, s, a, b, c;
	scanf("%d%d", &n, &s);
	for(int i=0; i<n; i++) {
		scanf("%d%d%d", &a, &b, &c);
		node[a]={a, b, c, false};		//还有这种操作?! 
	}
	for(int i=s; i!=-1; i=node[i].next) {
		node[i].flag=true;
		cnt++;
	} 
	if(cnt==0) {
		printf("0 -1\n");
	} else {
		sort(node, node+maxn, cmp);
		printf("%d %05d\n", cnt, node[0].address);
		for(int i=0; i<cnt; i++) {
			printf("%05d %d ", node[i].address, node[i].key);
			if(i<cnt-1) printf("%05d\n", node[i+1].address);
			else printf("-1\n");
		}
	}
	return 0;
}

A 1097-------*****

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100010;
bool hashtable[100010]={false};		//false表示没出现过,true表示出现过
struct Node{
	int address, key, next;
	int order=2*maxn;
}node[maxn];
bool cmp1(Node a, Node b) {
	return a.order<b.order;
}
int main() {
	int first, n, a, b, c, cnt=0;
	scanf("%d%d", &first, &n);
	for(int i=0; i<n; i++) {
		scanf("%d", &a);
		scanf("%d%d", &node[a].key, &node[a].next);
		node[a].address=a;
	}
	int cnt_valid=0, cnt_removed=0;
	for(int p=first; p!=-1; p=node[p].next) {
		if(hashtable[abs(node[p].key)]==false) {
			node[p].order=cnt_valid++;
			hashtable[abs(node[p].key)]=true;
		} else {
			node[p].order=maxn+cnt_removed++;
		}
	}
	sort(node, node+maxn, cmp1);
	cnt=cnt_valid+cnt_removed;
	for(int i=0; i<cnt; i++) {
		printf("%05d %d ", node[i].address, node[i].key);
		if(i==cnt_valid-1 || i==cnt-1) {
			printf("-1\n");
		} else {
			printf("%05d\n", node[i+1].address);
		}
	}
	return 0;
} 

A1103

问题总结:1.由于知道这道题是用DFS,但是一开始还是想不明白怎么使用,发现自己对于递归的使用还是很不明朗,其实对于递归只需要弄清楚几件事情:1)递归的边界,有时候可能会有多个出口;2)递归的调用,什么时候调用,参数如何变化;3)递归参数的选择;4)递归调用有几种情况;
弄清楚这些东西,基本上一个递归也就确定了。
2。题目没有对底数设限,但实际上通过思考可以得到底数一定是小于n的,书中的考虑是直接把1-n的p次方直接打印出来放在了vector中,由于这道题的数据不大,因此是可以的,这也是一种思想,即打表。打完表这道题实质上就变成了书中的选数问题了,且可以重复选择,因此对于递归调用的两个分支要注意一下!
3.递归的形参:首先肯定要一个index来选数,然后还需要nowk记录当前已选择的数的个数,还有sum,由于题目要求选出底数最小的,因此还需要facum作为底数之和。注意题目要求的是若底数和相同,输出大数,因此==可以考虑从大数开始枚举,这样就肯定可以选出最大的数啦!!!
注意k要求是正整数,因此index实际上是要大于等于1的!!!
以下为书中代码:

#include <iostream>
#include <vector> 
using namespace std;
vector<int> temp, ans, power;
int n, k, p;
int maxvalue=-1;
int squ(int a) {
	int ans=1; 
	for(int i=0; i<p; i++) {
		ans *= a;
	}
	return ans;
}
void init() {
	int temp=0, i=0;
	while(temp<=n) {
		power.push_back(temp);
		temp=squ(++i);
	}
}
void DFS(int index, int nowk, int sum, int facsum) {
	if(nowk==k && sum==n) {
		if(facsum>maxvalue) {
			ans=temp;
			maxvalue=facsum;
		}
		return;
	}
	if(nowk>k || sum>n || index==0) return;
	temp.push_back(index);
	DFS(index, nowk+1, sum+power[index], facsum+index);
	temp.pop_back();
	DFS(index-1, nowk, sum, facsum);
}
int main() {
	scanf("%d%d%d", &n, &k, &p);
	init();
	DFS(power.size()-1, 0, 0, 0);
	if(maxvalue==-1) printf("Impossible\n");
	else {
		printf("%d = ", n);
		for(int i=0; i<ans.size(); i++) {
			printf("%d^%d", ans[i], p);
			if(i<ans.size()-1) printf(" + ");
		}
		printf("\n");
	}
	return 0;
}

A1091

思路总结:1.这题与书中的例题相比,二维变成了三维,同时要求计算相邻1的总和,且得满足一定条件。发现自己只能照着书中的写,至于如何改进,还是没有头绪。
2.增量数组直接设为三个,也就是说看成三维数组来处理。matrix[x,y,z]输入的时候先枚举矩阵的编号z然后再输入x,y
同理main函数枚举可行点时也是按照这个方式。对于main函数中的可行点的判断,只需要考虑两点,一是矩阵该处是否为1及该处没有入过队!
3.深度优先遍历的思路就是对于每一个点都进行深度优先遍历,这个依靠BFS()进行,然后对每个点的枚举是在main函数中进行的,同时不要忘了可行点的if判断
4.对于此题的求1的数量,可以这样做,对于每一个可行点,在BFS末尾,判断total是否超过了T,若超过则返回,否则返回0,同时直接在main函数中执行ans+= BFS(i, j, k);这个命令即可,注意在BFS()中total的计算只需要对每个出队的点++就可以了。

//广度优先搜索
#include <iostream>
#include <queue>
using namespace std;
struct node{
	int x, y, z;
}Node;

int m, n, L, T;
int matrix[1290][130][61];		//三维矩阵确实不会=.= 
bool ispassed[1290][130][61]={false};
int X[6]={0, 0, 0, 0, 1, -1};
int Y[6]={0, 0, 1, -1, 0, 0};
int Z[6]={1, -1, 0, 0, 0, 0}; 

bool judge(int x, int y, int z) {
	if(x>=m || x<0 || y>=n || y<0 || z>=L || z<0) return false;
	if(ispassed[x][y][z]==true || matrix[x][y][z]==0) return false;
	return true; 
}

int BFS(int x, int y, int z) {
	int total=0;		//计算当前块中1的个数 
	queue<node> Q;
	Node.x=x; Node.y=y;Node.z=z;
	Q.push(Node);
	ispassed[Node.x][Node.y][Node.z]=true;
	while(!Q.empty()) {
		node top=Q.front();
		Q.pop();
		total++;
		for(int i=0; i<6; i++) {
			int newX=top.x+X[i];
			int newY=top.y+Y[i];
			int newZ=top.z+Z[i];
			if(judge(newX, newY, newZ)) {
				Node.x=newX;Node.y=newY;Node.z=newZ;
				Q.push(Node);
				ispassed[newX][newY][newZ]=true;
			}
		}
	}
	if(total>=T) return total;
	else return 0;
}

int main() {
	scanf("%d%d%d%d", &m, &n, &L, &T);
	for(int k=0; k<L; k++) {
		for(int i=0; i<m; i++) {
			for(int j=0; j<n; j++) {
				scanf("%d", &matrix[i][j][k]);
			}
		}
	}
	int ans=0;
	for(int k=0; k<L; k++) {
		for(int i=0; i<m; i++) {
			for(int j=0; j<n; j++) {
				if(matrix[i][j][k]==1 && ispassed[i][j][k]==false) {
					ans+= BFS(i, j, k);
				}
			}
		}
	}
	printf("%d\n", ans);
	return 0;
}

A 1020

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int maxn=31;
int inorder[maxn], postorder[maxn];
vector<int> levelorder;
struct node{
	int data;
	node *lchild, *rchild;
};

//递归建立二叉树
node* create(int inL, int inR, int postL, int postR) {
	if(postL>postR) return NULL;
	node* root=new node;
	root->data=postorder[postR];
	int k;
	for(k=inL; k<=inR; k++) {
		if(inorder[k]==postorder[postR]) {
			break;
		}
	}
	int numLeft=k-inL;
	root->lchild=create(inL, k-1, postL, postL+numLeft-1);
	root->rchild=create(k+1, inR, postL+numLeft, postR-1);
	return root;
} 

void level_search(node* root) {
	queue<node*> q;
	q.push(root);
	while(!q.empty()) {
		node* now=q.front();
		q.pop();
		levelorder.push_back(now->data);
		if(now->lchild!=NULL) q.push(now->lchild);
		if(now->rchild!=NULL) q.push(now->rchild);
	}
	for(int i=0; i<levelorder.size(); i++) {		//打印层序数组 
		printf("%d", levelorder[i]);
		if(i<levelorder.size()-1) printf(" ");
	}
}

int main() {
	int n;
	scanf("%d", &n);
	for(int i=1; i<=n; i++) {
		scanf("%d", &postorder[i]);
	}
	for(int i=1; i<=n; i++) {
		scanf("%d", &inorder[i]);
	}
	node* root=create(1, n, 1, n);
	level_search(root);
	return 0;
} 

A 1086

问题总结:1.发现自己总是重复定义变量,导致出错,找好久才找到,写的时候认真一点就不会出这种低级错误了=.=
2.一个测试点无法通过,估计是什么边界点没有考虑到,一开始想着的可能是空树没考虑,但是发现特判后还是不对,只有一个根节点的自己也能通过啊,
》》》》》发现原因是使用string不但出现问题!以后在使用string的时候要谨慎

#include <iostream>
#include <string>
#include <stack>
using namespace std;
const int maxn=31;
int inorder[maxn], preorder[maxn];
int num=0, n;
struct node{
	int data;
	node *lchild, *rchild;
};

node* create(int preL, int preR, int inL, int inR) {
	if(preL>preR) return NULL;
	node* root=new node;
	root->data=preorder[preL];
	int k;
	for(k=inL; k<=inR; k++) {
		if(inorder[k]==preorder[preL]) {
			break;
		}
	}
	
	int numLeft=k-inL;
	root->lchild=create(preL+1, preL+numLeft, inL, k-1);
	root->rchild=create(preL+numLeft+1, preR, k+1, inR);
	return root;
}

void Post_Search(node* root) {
	if(root==NULL) return;
	Post_Search(root->lchild);
	Post_Search(root->rchild);
	printf("%d", root->data);
	num++;
	if(num<n) printf(" ");
}

int main() {
	scanf("%d", &n);
	getchar();
	stack<int> st;
	int num, j=0, k=0;
	for(int i=0; i<2*n; i++) {
		string str;
		cin >> str;
		if(str=="Push") {
			cin >> num;
			preorder[j++]=num;
			st.push(num);
		} else {
			inorder[k++]=st.top();
			st.pop();
		}
	}
	node* root=create(0, n-1, 0, n-1);
	Post_Search(root);
	return 0;
}

A 1102

问题总结:1.一开始想着使用二叉链表做,但是发现不知道如何输入,因为又有数字还有横线。书中是采用静态数组做的。定义结构体的时候直接把指针定义为int型,然后定义Node[maxn]。
2.寻找根节点:即各结点的子节点中未出现的那个就是根结点,因此可以开一个hashtable[maxn]来记录已出现的结点,之后遍历一遍就可以知道根结点的下标。
3.对于什么时候标记结点,可以在把字符转成数字及-1的时候进行记录。
4.使用后序遍历可以对二叉树进行倒置,
5.对于数组表示的二叉树,各个遍历的递归出口是root==-1,注意!!!
6.将数组倒置后进行中序遍历和层次序遍历就可以得到结果了
7.对于数组表示的层次序,注意建立的queue< int > q;push进入的是下标!!!!
以下为书中代码:

#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=110; 
struct node{
	int lchild, rchild;
}Node[maxn]; 
bool notRoot[maxn]={false};
int n, num=0;//num为当前已输出的结点个数
void print(int id) {
	printf("%d", id);
	num++;
	if(num<n) printf(" ");
	else printf("\n");
} 

void inorder(int root) {
	if(root==-1) return;
	inorder(Node[root].lchild);
	print(root);
	inorder(Node[root].rchild);
}
void BFS(int root) {
	queue<int> q;
	q.push(root);
	while(!q.empty()) {
		int now=q.front();
		q.pop();
		print(now);
		if(Node[now].lchild!=-1) q.push(Node[now].lchild);
		if(Node[now].rchild!=-1) q.push(Node[now].rchild);
	}
}

void Post_Search(int root) {
	if(root==-1) return;
	Post_Search(Node[root].lchild);
	Post_Search(Node[root].rchild);
	swap(Node[root].lchild, Node[root].rchild);
}

int strtonum(char c){
	if(c=='-') return -1;
	else {
		notRoot[c-'0']=true;
		return c-'0';
	}
}

int findRoot() {
	for(int i=0; i<n; i++) {
		if(notRoot[i]==false) {
			return i;
		}
	}
}

int main() {
	char lchild, rchild;
	scanf("%d", &n);
	for(int i=0; i<n; i++) {
		scanf("%*c%c %c", &lchild, &rchild);
		Node[i].lchild=strtonum(lchild);
		Node[i].rchild=strtonum(rchild);
	}
	int root=findRoot();
	Post_Search(root);
	BFS(root);
	num=0;
	inorder(root);
	return 0;
}

A1053

问题总结:1.关于输出结果如何非递减排序的问题:发现这一类问题都可以在初始化或者输入的时候通过特定的方式解决,而不必在最后进行排序(当然也会有这种可能),此题的方式是初始的时候对每个节点的子节点vector进行排序,即对vector中的结点按照递减的顺序排好!!!,这样的话当得到一组数据的时候,就可以直接输出啦!!不用另外来输出!
2.书中使用了数组来存储dfs便利过程中的结点编号,DFS过程中的变量:当前访问的结点编号index, 当前路径path上的结点个数numNode, 还有当前路径上的权值和sum,
3.在递归过程中保存路径,有两种方式:一种是采用path[]数组,一种是使用vector。
以下为书中代码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=110;
struct node{
	int weight;
	vector<int> child;
}Node[maxn];
bool cmp(int a, int b) {
	return Node[a].weight>Node[b].weight;
}
int n, m, s;
int path[maxn];
void DFS(int index, int numNode, int sum) {
	if(sum>s) return;
	else if(sum==s) {
		if(Node[index].child.size()!=0) return;
		else {
			for(int i=0; i<numNode; i++) {
				printf("%d", Node[path[i]].weight);
				if(i<numNode-1) printf(" ");
			}
			printf("\n");
		}
		return;
	}
	for(int i=0; i<Node[index].child.size(); i++) {
		int child=Node[index].child[i];		//孩子结点的编号 
		path[numNode]=child;
		DFS(child, numNode+1, sum+Node[child].weight);
	}
}

int main() {
	int temp, child_num, child_order;		//temp表示读入的非叶节点的序号,child_num表示该非叶节点的孩子数目 
	scanf("%d%d%d", &n, &m, &s);
	for(int i=0; i<n; i++) {
		scanf("%d", &Node[i].weight);
	}
	for(int i=0; i<m; i++) {			//读入孩子 
		scanf("%d%d", &temp, &child_num);
		for(int j=0; j<child_num; j++) {
			scanf("%d", &child_order);
			Node[temp].child.push_back(child_order);
		}
		sort(Node[temp].child.begin(), Node[temp].child.end(), cmp); 
	}
	//开始遍历
	path[0]=0;
	DFS(0, 1, Node[0].weight);
	return 0;
} 

A 1079

总结:1.?好像是第一道自己写出来的树遍历的题,虽然还是有两个测试点没通过,还是好开心鸭!!,发现超时的原因是对所有的结点都计算了各自的Price,修改后就通过啦,虽然代码一点也不优美=.=
2.由于正好是double类型的数据,可以直接使用power函数,不需要自己实现一遍!记得添加头文件math.h
已通过OJ

#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const int maxn=100010;
struct node{
	double price;
	int number, level;			//number是叶结点的产品的数量,非叶结点为0。各结点的层数
	vector<int> child;
}Node[maxn];
int n;
double p, r;
double ans=0;
double cal_price(int level) {
	double result=p;
	if(level==1) {
		return result;
	} else {
		for(int i=0; i<level-1; i++) {
			result*=(1+r*0.01);
		}
		return result;
	}
	
} 

void LevelOrder(int root) {
	queue<int> Q;
	Q.push(root);
	while(!Q.empty()) {
		int now=Q.front(); 
		if(Node[now].child.size()==0) {
			Node[now].price=p*pow(1+r, Node[now].level-1);
		}
		//Node[now].price=cal_price(Node[now].level);
		Q.pop();
		for(int i=0; i<Node[now].child.size(); i++) {
			int child=Node[now].child[i];
			Node[child].level=Node[now].level+1;
			Q.push(child);
		}
	} 
} 

int main() {
	int amount, child_num;
	scanf("%d%lf%lf", &n, &p, &r);
	r=r*0.01;
	Node[0].price=p;
	for(int i=0; i<n; i++) {
		scanf("%d", &amount);
		if(amount==0) {
			scanf("%d", &Node[i].number);
		} else {
			for(int j=0; j<amount; j++) {
				scanf("%d", &child_num);
				Node[i].child.push_back(child_num);
				Node[i].number=0; 
			}
		}
	}
	//层次序遍历得到各非叶结点的层数及每个结点从父结点购买的价格 
	Node[0].level=1;
	LevelOrder(0);
	for(int i=0; i<n; i++) {
		if(Node[i].child.size()==0) {
			ans += Node[i].price*Node[i].number;
		}
	}
	printf("%.1f\n", ans);
	return 0;
}

以下为书中代码:书中的是采用dfs的方法做的:
注意:DFS的变量有两个,一个是depth, 还一个是当前结点编号index

#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
const int maxn=100010;
struct node{
	double data;
	vector<int> child;
}Node[maxn];
int n;
double p, r, ans=0;
void DFS(int index, int depth) {
	if(Node[index].child.size()==0) {
		ans += Node[index].data*pow(1+r, depth);
		return;
	}
	for(int i=0; i<Node[index].child.size(); i++) {
		DFS(Node[index].child[i], depth+1);
	}
} 

int main() {
	int k, child;
	scanf("%d%lf%lf", &n, &p, &r);
	r/=100;
	for(int i=0; i<n; i++) {
		scanf("%d", &k);
		if(k==0) {
			scanf("%lf", &Node[i].data);
		} else {
			for(int j=0; j<k; j++) {
				scanf("%d", &child);
				Node[i].child.push_back(child);
			}
		}
	}
	DFS(0, 0);
	printf("%.1f\n", p*ans);
	return 0;
}

A1090

总结:1.此题和上面的这题题干是一样的,主要问题是一开始没看懂题目的树是怎么构建的,想了10分钟才想明白,然后此题用了DFS做,比BFS要简单,毕竟递归!!
已通过OJ

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=100010;
struct node{
	double data;
	vector<int> child;
}Node[maxn];
int n, count=0;
double p, r, ans=0, temp;
void DFS(int index, int depth) {
	if(Node[index].child.size()==0) {
		temp=p*pow(1+r, depth);
		if(temp>ans) {
			ans=temp;
			count=1;
		} else if(temp==ans) {
			count +=1;
		}
		return;
	}
	for(int i=0; i<Node[index].child.size(); i++) {
		DFS(Node[index].child[i], depth+1);
	}
}

int main() {
	int temp, first;
	scanf("%d%lf%lf", &n, &p, &r);
	r/=100;
	for(int i=0; i<n; i++) {
		scanf("%d", &temp);
		if(temp==-1) {
			first=i;
		} else {
			Node[temp].child.push_back(i);
		}
	}
	DFS(first, 0);
	printf("%.2f %d\n", ans, count);
}
//按照书中的代码将自己的代码优化过后:
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=100010;
vector<int> child[maxn]; 
int n, count=0;
double p, r, maxdepth=0;
void DFS(int index, int depth) {
	if(child[index].size()==0) {
		if(depth>maxdepth) {
			maxdepth=depth;
			count=1;
		} else if(maxdepth==depth) {
			count++;
		}
		return;
	}
	for(int i=0; i<child[index].size(); i++) {
		DFS(child[index][i], depth+1);
	}
}

int main() {
	int temp, first;
	scanf("%d%lf%lf", &n, &p, &r);
	r/=100;
	for(int i=0; i<n; i++) {
		scanf("%d", &temp);
		if(temp==-1) {
			first=i;
		} else {
			child[temp].push_back(i);
		}
	}
	DFS(first, 0);
	printf("%.2f %d\n", p*pow(1+r, maxdepth), count);
}

A1094

总结:1.此题也可以用DFS做,代码会更简单!
已通过OJ

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int maxn=100;
struct node{
	int level;
	vector<int> child;
}Node[maxn];
int n, m;
int level[maxn]={0}, ans=1;
void levelOrder(int root) {
	queue<int> Q;
	Q.push(root);
	Node[1].level=1;
	while(!Q.empty()) {
		int now=Q.front();
		level[Node[now].level]++;
		Q.pop();
		for(int i=0; i<Node[now].child.size(); i++) {
			int child=Node[now].child[i];
			Q.push(child);
			Node[child].level=Node[now].level+1;
		}
	}
}

int main() {
	int id, num_child, temp;
	scanf("%d%d", &n, &m);
	for(int i=0; i<m; i++) {
		scanf("%d%d", &id, &num_child);
		for(int j=0; j<num_child; j++) {
			scanf("%d", &temp);
			Node[id].child.push_back(temp);
		}
	}
	//对树进行层次遍历,同时对每层的人数计数
	levelOrder(1);
	for(int i=0; i<maxn; i++) {
		if(level[i]>level[ans]) {
			ans=i;
		}
	}
	printf("%d %d", level[ans], ans);
	return 0;
}

A1106

总结:1.耽误了十分钟,原因在于输入数据的地方少了个大循环,服了自己了=.=
已通过OJ

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=100010;
struct node{
	int data;
	vector<int> child;
}Node[maxn];
int n;
double p, r;
int mindepth=maxn, count;
void DFS(int index, int depth) {
	if(Node[index].child.size()==0) {
		if(depth<mindepth) {
			mindepth=depth;
			count=1;
		} else if(depth==mindepth) {
			count++;
		}
		return;
	}
	for(int i=0; i<Node[index].child.size(); i++) {
		DFS(Node[index].child[i], depth+1);
	}
}

int main() {
	int num_child, temp;
	scanf("%d%lf%lf", &n, &p, &r);
	r/=100;
	for(int i=0; i<n; i++) {
		scanf("%d", &num_child);
		if(num_child!=0) {
			for(int j=0; j<num_child; j++) {
				scanf("%d", &temp);
				Node[i].child.push_back(temp);
			}
		}
	}
	//DFS遍历
	DFS(0, 0);
	printf("%.4f %d\n", p*pow(1+r, mindepth), count);
	return 0;
} 

A 1004

总结:1.注意是单点测试,一开始没看清楚题目还以为是多点呢=.=.
已通过OJ

#include <iostream>
#include <vector>
using namespace std;
const int maxn=100;
struct node{
	int data;
	vector<int> child;
}Node[maxn];
int n, m;
//采用DFS
int hashtable[maxn]={0};
int maxdepth=0;
void DFS(int index, int depth) {
	if(Node[index].child.size()==0) {
		hashtable[depth]++;
		if(depth>maxdepth) maxdepth=depth;
	}
	for(int i=0; i<Node[index].child.size(); i++) {
		DFS(Node[index].child[i], depth+1);
	}
}

int main() {
	int id, num_child, temp;
	scanf("%d", &n);
	if(n!=0) {
		scanf("%d", &m);
		for(int i=0; i<m; i++) {
			scanf("%d%d", &id, &num_child);
			for(int j=0; j<num_child; j++) {
				scanf("%d", &temp);
				Node[id].child.push_back(temp);
			}
		}
		DFS(1, 1);
		for(int i=1; i<=maxdepth; i++) {
			printf("%d", hashtable[i]);
			if(i<maxdepth) printf(" ");
		}
		printf("\n");
	}
	return 0;
} 

A 1043

问题总结:1.一开始不知道思路。其实挺简单的:将输入序列使用插入算法得到一棵二叉排序树,并对其进行先序遍历或者是镜像的先序遍历,并与初始的输入序列比较,若相同则yes,并输出其后序,注意后序遍历也得分两种来写。否则输出no,
2.关于vector比较大小:注意!vector可以直接比较大小!!,即可以直接判断两个向量是否完全相同!还可以进行><等compare,按字典序比较!!
以下为书中代码:
这题说实话,不难!!

#include <iostream>
#include <vector>
using namespace std;
const int maxn=1010;
struct node{
	int data;
	node *lchild, *rchild;
}Node[maxn];
//BST的插入操作 
int n;
vector<int> origin, pre, preM, post, postM;
void insert(node* &root, int x) {
	if(root==NULL) {
		root=new node;
		root->data=x;
		root->lchild=root->rchild=NULL;
		return;
	}
	if(root->data==x || root->data<x) {
		insert(root->rchild, x);
	} else if(root->data>x){
		insert(root->lchild, x);
	}
}
void preorder(node* root) {
	if(root==NULL) return;
	pre.push_back(root->data);
	preorder(root->lchild);
	preorder(root->rchild);
}
void postorder(node* root) {
	if(root==NULL) return;
	postorder(root->lchild);
	postorder(root->rchild);
	post.push_back(root->data);
}
void preorderMirror(node* root) {
	if(root==NULL) return;
	preM.push_back(root->data);
	preorderMirror(root->rchild);
	preorderMirror(root->lchild);
}
void postorderMirror(node* root) {
	if(root==NULL) return;
	postorderMirror(root->rchild);
	postorderMirror(root->lchild);
	postM.push_back(root->data);
}

int main() {
	int temp;
	scanf("%d", &n);
	node* root=NULL;
	for(int i=0; i<n; i++) {
		scanf("%d", &temp);
		origin.push_back(temp);
		insert(root, temp);
	}
	preorder(root);
	preorderMirror(root);
	postorder(root);
	postorderMirror(root);
	if(origin==pre) {
		printf("YES\n");
		for(int i=0; i<n; i++) {
			printf("%d", post[i]);
			if(i<n-1) printf(" ");
		}
		printf("\n");
	} else if(origin==preM) {
		printf("YES\n");
		for(int i=0; i<n; i++) {
			printf("%d", postM[i]);
			if(i<n-1) printf(" ");
		}
		printf("\n");
	} else {
		printf("NO\n");
	}
	return 0;
}

A 1064

问题总结:1.看清题目再开始做题!!!
2.由于看错题目,直接把CBT部分的内容直接忽视了!!!!服了,我说这道30分的题怎么这么简单!
3.关于完全二叉树的,真的没有思路。
4.书中思路:首先采用一个一维数组对完全二叉树进行保存,其顺序刚好就是该树的层次序。然后又有BST的中序是递增序列,因此如果我们对输入序列进行排序,然后对数组进行中序遍历(发现这里不会),同时按排序后的数组一个个赋值就可以得到结果!!
5.对于完全二叉树的数组:1)结点从下标1开始保存!
以下为书中代码:

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1010;
int n, number[maxn], CBT[maxn], index=0;
//对数组进行中序遍历
void inorder(int root) {
	if(root>n) return;
	inorder(root*2);
	CBT[root]=number[index++];
	inorder(root*2+1);
} 

int main() {
	scanf("%d", &n);
	for(int i=0; i<n; i++) {
		scanf("%d", &number[i]);
	}
	sort(number, number+n);
	inorder(1);
	for(int i=1; i<=n; i++) {
		printf("%d", CBT[i]);
		if(i<n) printf(" "); 
	}
	printf("\n");
	return 0;
}

A 1099–此题与上一题思想是一样的

总结:1.由于是二叉树,使用树的静态写法会比较方便,所以在定义树的结点的时候,直接把其lchild和rchild的类型定义为int即可!!刚好可以直接输入!-1表示空!
已通过OJ

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=110;
struct node{
	int data;
	int lchild, rchild;
}Node[maxn];
int n, index=0;
vector<int> number, level;
//对树进行中序遍历
void inorder(int root) {
	if(root==-1) return;
	inorder(Node[root].lchild);
	Node[root].data=number[index++];
	inorder(Node[root].rchild);
}
//对树进行层次序遍历 
void levelorder(int root) {
	queue<int> Q;
	Q.push(root);
	int count=0;
	while(!Q.empty()) {
		int now=Q.front();
		printf("%d", Node[now].data);
		count++;
		if(count<n) printf(" ");
		Q.pop();
		if(Node[now].lchild!=-1) Q.push(Node[now].lchild);
		if(Node[now].rchild!=-1) Q.push(Node[now].rchild);
	}
}

int main() {
	int temp;
	scanf("%d", &n);
	for(int i=0; i<n; i++) {
		scanf("%d %d", &Node[i].lchild, &Node[i].rchild);
	}
	for(int i=0; i<n; i++) {
		scanf("%d", &temp);
		number.push_back(temp);
	}
	sort(number.begin(), number.end());
	inorder(0);
	levelorder(0);
	return 0;
}
 

A 1066

总结:1.此题的套路就是书中的AVL模板,照着写就可以,主要是麻烦,代码特别长。
2.有时候不会做可以骗分=.=

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=30;
struct node{
	int v, height;
	node *lchild, *rchild;
};
int data[maxn], n;
int getheight(node* root) {
	if(root==NULL) return 0;
	return root->height;
}
void updateheight(node* root) {
	root->height=max(getheight(root->lchild), getheight(root->rchild))+1;
}
int getbalanceFactor(node* root) {
	return getheight(root->lchild)-getheight(root->rchild);
}
void L(node* &root) {
	node* temp=root->rchild;
	root->rchild=temp->lchild;
	temp->lchild=root;
	updateheight(root);
	updateheight(temp);
	root=temp;
}
void R(node* &root) {
	node* temp=root->lchild;
	root->lchild=temp->rchild;
	temp->rchild=root;
	updateheight(root);
	updateheight(temp);
	root=temp;	
}
node* newNode(int v) {
	node* temp=new node;
	temp->v=v;
	temp->height=1;
	temp->lchild=temp->rchild=NULL;
	return temp;
}
void insert(node* &root, int v) {
	if(root==NULL) {
		root=newNode(v);
		return;
	}
	if(root->v > v) {
		insert(root->lchild, v);
		updateheight(root);
		if(getbalanceFactor(root)==2) {
			if(getbalanceFactor(root->lchild)==1) {
				R(root);
			} else if(getbalanceFactor(root->lchild)==-1) {
				L(root->lchild);
				R(root);
			}
		} 
	} else {
		insert(root->rchild, v);
		updateheight(root);
		if(getbalanceFactor(root)==-2) {
			if(getbalanceFactor(root->rchild)==-1) {
				L(root);
			} else if(getbalanceFactor(root->rchild)==1) {
				R(root->rchild);
				L(root);
			}
		}
	}
}

node* create(int data[], int n) {
	node* root=NULL;
	for(int i=0; i<n; i++) {
		insert(root, data[i]);
	}
	return root;
}

int main() {
	scanf("%d", &n);
	for(int i=0; i<n; i++) {
		scanf("%d", &data[i]);
	}
	node* root=create(data, n);
	printf("%d\n", root->v);
	return 0;
}
//中位数--17分,骗分操作=.= 
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=30;
int data[maxn], n;

int main() {
	scanf("%d", &n);
	for(int i=0; i<n; i++) {
		scanf("%d", &data[i]);
	}
	sort(data, data+n);
	printf("%d\n", data[n/2]);
	return 0;
}

A 1107

问题总结:1.想不清楚该如何使用并查集,感觉很绕。思路其实也不难:使用一个数组course[n]来记录任意一个喜欢活动 h的人的编号,这样的话findfather(course[h])就是这个人所在社交网络的根结点。所以对当前读入的人的编号i和他喜欢的一个活动h,只需要合并i与findfather(course[h])即可。
2.思考时的问题:不知道该合并什么东西,对于并查集还不太熟练,其实要合并的只是每个有相同爱好的人的编号,对于其爱好的编号,新开一个数组course[maxn],用来记录每次读入的爱好编号是否与其他人相同,若没有,则令course[index]=i;
然后无论有没有,都合并i和findfather(course[index]),—思路是:如果爱好相同则将二人的编号合并,若爱好不同,则将该人的编号与此人的根结点合并;
3.统计的时候常开一数组isRoot[maxn]来统计每个人的根结点的出现次数,然后排序,打印
以下为书中代码:

#include <iostream>
#include <algorithm>
using namespace std; 
const int maxn=1010;
int father[maxn];
int isRoot[maxn]={0}, course[maxn]={0};
int n;
void init(int n) {
	for(int i=1; i<=n; i++) {
		father[i]=i;
	}
}
int findfather(int x) {
	if(x==father[x]) {
		return x;
	} else {
		int f=findfather(father[x]);
		father[x]=f;
		return f;
	}
}

void unions(int a, int b) {
	int faA=findfather(a);
	int faB=findfather(b);
	if(faA!=faB) {
		father[faA]=faB;
	}
}

bool cmp(int a, int b) {
	return a>b;
}
int main() {
	int temp, index;
	scanf("%d", &n);
	init(n);
	for(int i=1; i<=n; i++) {
		scanf("%d:", &temp);
		for(int j=0; j<temp; j++) {
			scanf("%d", &index);
			if(course[index]==0) {
				course[index]=i;
			}
			unions(i, findfather(course[index]));
		}
	}
	for(int i=1; i<=n; i++) {
		isRoot[findfather(i)]++;
	}
	int ans=0;
	for(int i=1; i<=n; i++) {
		if(isRoot[i]!=0) {
			ans++;
		}
	}
	printf("%d\n", ans);
	sort(isRoot+1, isRoot+n+1, cmp);
	for(int i=1; i<=ans; i++) {
		printf("%d", isRoot[i]);
		if(i<ans) printf(" ");
	}
	return 0;
	
} 

A 1098

总结:1.忘记了插入算法是如何实现的;对于堆的插入排序还是不熟啊!!!!
已通过OJ

#include <iostream>
#include <vector>
using namespace std;
const int maxn=110;
int n;
int heap[maxn], partial[maxn], insertion[maxn];
void downAdjust(int low, int high) {
	int i=low, j=i*2;
	while(j<=high) {
		if(j+1<=high && heap[j+1]>heap[j]) {
			j=j+1;
		} 
		if(heap[i]<heap[j]) {
			swap(heap[i], heap[j]);
			i=j;j=i*2;
		} else {
			break;
		}
	}
}
void createheap() {
	for(int i=n/2; i>=1; i--) {
		downAdjust(i, n);
	}
}
bool isEqual(int a[], int b[]) {
	int i;
	for(i=1; i<=n; i++) {
		if(a[i]!=b[i]) {
			break;
		}
	}
	if(i<=n) return false;
	else return true;
}

bool heapSort() {
	createheap();
	bool flag=true;
	for(int i=n; i>=1; i--) {
		swap(heap[i], heap[1]);
		downAdjust(1,i-1);
		if(flag==false) {
			return true;
		}
		if(isEqual(heap, partial)) flag=false;
	}
	return false;
}

bool insertSort() {
	int flag=true;
	for(int i=2; i<=n; i++) {
		int temp=insertion[i], j=i;
		while(j>1 && insertion[j-1]>temp) {
			insertion[j]=insertion[j-1];
			j--;
		}
		insertion[j]=temp;
//		for(int i=1; i<=n; i++) {
//			printf("%d ", insertion[i]);
//		}
		//printf("insertion?=partial=%d\n", isEqual(insertion, partial));
		if(flag==false) {
			return true;
		}
		if(isEqual(insertion, partial)) flag=false;
	}
	return false;
}

int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++) {
		scanf("%d", &heap[i]);
		insertion[i]=heap[i];
	}
	for(int i=1; i<=n; i++) {
		scanf("%d", &partial[i]);
	}
	createheap();
	if(heapSort()==true) {
		printf("Heap Sort\n");
		for(int i=1; i<=n; i++) {
			printf("%d", heap[i]);
			if(i<n) printf(" ");
		}
	} else if(insertSort()==true) {
		printf("Insertion Sort\n");
		for(int i=1; i<=n; i++) {
			printf("%d", insertion[i]);
			if(i<n) printf(" "); 
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值