UVA572 Oil Deposits DFS求解
小白书上经典DFS题目。
1. 递归实现
// from: https://www.cnblogs.com/huaszjh/p/4686092.html
#include <stdio.h>
#include <string.h>
#define maxn 105
unsigned char data[maxn][maxn];
int m, n, vis[maxn][maxn];
void dfs(int x, int y, int ans) {
	if (x < 0 || x >= m || y < 0 || y >= n) return; //出界
	if (vis[x][y] > 0 || data[x][y] == '*') return; //非'@'或已经访问
	vis[x][y] = ans; //连通分量编号
	for (int k = -1; k <= 1; k++) {
		for (int t = -1; t <= 1; t++) {
			if (k != 0 || t != 0) { //自身格子不需要重复判断
				dfs(x + k, y + t, ans);
			}
		}
	}
}
#define DEBUG
int main() {
#ifdef DEBUG
	const char* input_txt_pth = "F:/zhangzhuo/debug/OJ/UVA-572.txt";
	freopen(input_txt_pth, "r", stdin);
#endif
	int i, j;
	while (scanf("%d %d", &m, &n) && m &&n) {
		int count = 0; //连通块
		memset(vis, 0, sizeof(vis));
		for (i = 0; i < m; i++) {
			scanf("%s", data[i]);
		}
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				//对未访问且为`@`的格子进行访问
				if (vis[i][j] == 0 && data[i][j] == '@') {
					dfs(i, j, ++count);
				}
			}
		}
		printf("%d\n", count);
#ifdef DEBUG
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				printf("%3d", vis[i][j]);
			}
			printf("\n");
		}
		printf("\n");
#endif
	}
	return 0;
}
2. 递归dfs函数用迭代实现
每个节点的dfs递归调用,改成用stack容器就地计算,是个while循环,本质上还是栈,但是避免了递归时嵌套产生的开销造成的潜在风险。
C++的stack、vector容器用起来比较顺手。另外就是把坐标简单封装为一个结构体。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stack>
#include <vector>
typedef struct Coord {
	char x, y;
} Coord;
#define DEBUG
int main() {
#ifdef DEBUG
	const char* input_txt_pth = "F:/zhangzhuo/debug/OJ/UVA-572.txt";
	freopen(input_txt_pth, "r", stdin);
#endif
	int m, n, i, j;
	#define maxn 105
	unsigned char data[maxn][maxn];
	int vis[maxn][maxn];
	while (scanf("%d %d", &m, &n) && m &&n) {
		int count = 0; //连通块
		memset(vis, 0, sizeof(vis));
		for (i = 0; i < m; i++) {
			scanf("%s", data[i]);
		}
		std::stack<Coord> stk;
		Coord cd;
		std::vector<Coord>offset;
		cd.x = -1; cd.y = -1; offset.push_back(cd);
		cd.x = -1; cd.y = 0; offset.push_back(cd);
		cd.x = -1; cd.y = 1; offset.push_back(cd);
		cd.x = 0; cd.y = -1; offset.push_back(cd);
		cd.x = 0; cd.y = 1; offset.push_back(cd);
		cd.x = 1; cd.y = -1; offset.push_back(cd);
		cd.x = 1; cd.y = 0; offset.push_back(cd);
		cd.x = 1; cd.y = 1; offset.push_back(cd);
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				cd.x = i; cd.y = j;
				if (vis[cd.x][cd.y] > 0 || data[cd.x][cd.y] != '@') continue;
				count++;
				stk.push(cd);
				while (!stk.empty()) {
					cd = stk.top();
					stk.pop();
					vis[cd.x][cd.y] = count;
					Coord tmp;
					for (size_t k = 0; k < offset.size(); k++) {
						tmp.x = cd.x + offset[k].x;
						tmp.y = cd.y + offset[k].y;
						if (tmp.x < 0 || tmp.x >= m || tmp.y < 0 || tmp.y >= n) continue;
						if (vis[tmp.x][tmp.y] > 0 || data[tmp.x][tmp.y] != '@') continue;
						stk.push(tmp);
					}
				}
			}
		}
		printf("%d\n", count);
#ifdef DEBUG
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				printf("%3d", vis[i][j]);
			}
			printf("\n");
		}
		printf("\n");
#endif
	}
	return 0;
}
3.纯C,DFS非递归,自定义栈ADT,函数指针
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
typedef struct Coord Coord;
struct Coord {
	char x, y;
};
typedef struct CoordOffset CoordOffset;
struct CoordOffset {
	size_t num;
	int* x;
	int* y;
};
typedef struct ListNode ListNode;
struct ListNode
{
	ListNode* next;
	void* data;
};
typedef struct Stack Stack;
struct Stack {
	ListNode* head;
	size_t len;
	void(*push_coord)(Stack* stk, Coord* coord);
	void (*pop_coord)(Stack* stk);
	void (*top_coord)(Stack* stk, Coord* coord);
};
void stack_push_coord(Stack* stk, Coord* coord) {
	ListNode* new_head = (ListNode*)malloc(sizeof(ListNode));
	/* new_head->data = coord; */
	new_head->data = (Coord*)malloc(sizeof(ListNode));
	memcpy(new_head->data, coord, sizeof(Coord));
	new_head->next = stk->head;
	stk->head = new_head;
	stk->len++;
}
void stack_pop_coord(Stack* stk) {
	if (stk->head != NULL) {
		ListNode* new_head = stk->head->next;
		free(stk->head->data);
		free(stk->head);
		stk->head = new_head;
		stk->len--;
	}
}
void stack_top_coord(Stack* stk, Coord* coord) {
	if (stk->head != NULL) {
		Coord* t_coord = (Coord*)(stk->head->data);
		coord->x = t_coord->x;
		coord->y = t_coord->y;
	}
}
void make_stack(Stack** _stk) {
	Stack* stk = (Stack*)malloc(sizeof(Stack));
	stk->head = NULL;
	stk->len = 0;
	stk->push_coord = stack_push_coord;
	stk->pop_coord = stack_pop_coord;
	stk->top_coord = stack_top_coord;
	/* write back */
	*_stk = stk;
}
void free_stack(Stack* stk) {
	ListNode* cur = stk->head;
	ListNode* temp;
	size_t i;
	for (i = 0; i < stk->len; i++) {
		temp = cur->next;
		free(cur->data);
		free(cur);
		cur = temp;
	}
	free(stk);
	stk = NULL;
}
void make_8coord_offset(CoordOffset** _offset) {
	CoordOffset* offset = (CoordOffset*)malloc(sizeof(CoordOffset));
	offset->num = 8;
	offset->x = (int*)malloc(sizeof(int)*offset->num);
	offset->y = (int*)malloc(sizeof(int)*offset->num);
	offset->x[0] = -1; offset->y[0] = -1;
	offset->x[1] = -1; offset->y[1] =  0;
	offset->x[2] = -1; offset->y[2] =  1;
	offset->x[3] =  0; offset->y[3] = -1;
	offset->x[4] =  0; offset->y[4] =  1;
	offset->x[5] =  1; offset->y[5] = -1;
	offset->x[6] =  1; offset->y[6] =  0;
	offset->x[7] =  1; offset->y[7] =  1;
	/* write back */
	*_offset = offset;
}
void free_coord_offset(CoordOffset* offset) {
	if (offset) {
		if (offset->x) {
			free(offset->x);
			offset->x = NULL;
		}
		if (offset->y) {
			free(offset->y);
			offset->y = NULL;
		}
		free(offset);
		offset = NULL;
	}
}
/* #define DEBUG */
int main() {
#ifdef DEBUG
	const char* input_txt_pth = "F:/zhangzhuo/debug/OJ/UVA-572.txt";
	freopen(input_txt_pth, "r", stdin);
#endif
	int m, n, i, j;
	size_t k;
	#define maxn 105
	unsigned char data[maxn][maxn];
	int vis[maxn][maxn];
	/* here we use 8 neighbours */
	CoordOffset* offset = NULL;
	make_8coord_offset(&offset);
	while (scanf("%d %d", &m, &n) && m &&n) {
		int count = 0; /* 连通块 */
		memset(vis, 0, sizeof(vis));
		for (i = 0; i < m; i++) {
			scanf("%s", data[i]);
		}
		/* std::stack<Coord> stk; */
		Stack* stk;
		make_stack(&stk);
		Coord cd;
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				cd.x = i; cd.y = j;
				if (vis[cd.x][cd.y] > 0 || data[cd.x][cd.y] != '@') continue;
				count++;
				/* stk.push(cd); */
				stack_push_coord(stk, &cd);
				/* while (!stk.empty()) { */
				while(stk->len!=0) {
					/* cd = stk.top(); */
					/* stack_top_coord(stk, &cd); */
					stk->top_coord(stk, &cd);
					/* stk.pop(); */
					/* stack_pop_coord(stk); */
					stk->pop_coord(stk);
					vis[cd.x][cd.y] = count;
					Coord tmp;
					for (k = 0; k < offset->num; k++) {
						tmp.x = cd.x + offset->x[k];
						tmp.y = cd.y + offset->y[k];
						if (tmp.x < 0 || tmp.x >= m || tmp.y < 0 || tmp.y >= n) continue;
						if (vis[tmp.x][tmp.y] > 0 || data[tmp.x][tmp.y] != '@') continue;
						/* stk.push(tmp); */
						/* stack_push_coord(stk, &tmp); */
						stk->push_coord(stk, &tmp);
					}
				}
			}
		}
		free_stack(stk);
		printf("%d\n", count);
#ifdef DEBUG
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				printf("%3d", vis[i][j]);
			}
			printf("\n");
		}
		printf("\n");
#endif
	}
	free_coord_offset(offset);
	return 0;
}
4.DFS+并查集实现
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int fa[10500];
int m, n, cnt, vis[105][105];
char mp[105][105];
int find(int x) {
	if (fa[x] == x) return x;
	fa[x] = find(fa[x]);
	return fa[x];
}
void merge(int x, int y) {
	int fx = find(x);
	int fy = find(y);
	if (fx == fy) return;
	fa[fx] = fy;
}
void dfs(int x, int y, int fx, int fy) {
	if (x < 0 || x >= m || y < 0 || y >= n) return;
	if (vis[x][y] || mp[x][y] == '*') return;
	vis[x][y] = 1;
	/* cout<<"x || y || fx || fy : "<<x<<" || "<<y<<" || "<<fx<<" || "<<fy<<endl; */
	if (fx != -1) {
		merge(x*m + y, fx*m + fy);
	}
	int i, j;
	for (i = -1; i < 2; i++) {
		for (j = -1; j < 2; j++) {
			if (!i && !j) continue;
			dfs(x + i, y + j, x, y);
		}
	}
}
/* #define LOCAL */
int main() {
#ifdef LOCAL
	const char* input_txt = "F:/zhangzhuo/debug/OJ/UVA-572.txt";
	freopen(input_txt, "r", stdin);
#endif
	int i, j;
	while (scanf("%d%d", &m, &n) == 2 && m && n) {
		cnt = 0;
		memset(vis, 0, sizeof(vis));
		for (i = 0; i < m; i++) {
			scanf("%s", mp[i]);
		}
		for (i = 0; i < 10500; i++) {
			fa[i] = i;
		}
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				if (!vis[i][j] && mp[i][j] == '@') {
					dfs(i, j, -1, -1);
					cnt++;
				}
			}
		}
		printf("%d\n", cnt);
#ifdef LOCAL
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				printf("%3d", vis[i][j]);
			}
			printf("\n");
		}
		printf("\n");
#endif
	}
	return 0;
}
5.DFS+并查集+不使用全局变量+简单封装为结构体
修改自 UVA572 (并查集解法) 。这种写法有点问题:已经用了dfs,dfs里用并查集多此一举,如果用并查集就不应该递归调用dfs。
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct FSU_Node {
	int p;    /* parent id */
	int rank;
	int vis; /* group(connected component) id */
} FSU_Node;
/*
get node's root id
@param x: node id
@param nodes: all nodes in map
*/
int fus_find(int x, FSU_Node* nodes) {
	if (nodes[x].p == x) return x;
	nodes[x].p = fus_find(nodes[x].p, nodes);
	return nodes[x].p;
}
/*
merge two node groups
@param a: a node from one node group
@param b: a node from another node group
*/
void fus_union(int a, int b, FSU_Node* nodes)
{
	int ra = fus_find(a, nodes); /* ra: root id of a */
	int rb = fus_find(b, nodes); /* rb: root id of b */
	if (ra == rb) {
		return;
	}
	if (nodes[ra].rank > nodes[rb].rank)
	{
		nodes[rb].p = ra;
	}
	else {
		if (nodes[ra].rank == nodes[rb].rank)
		{
			nodes[rb].rank++;
		}
		nodes[ra].p = rb;
	}
}
typedef struct ImageSize {
	int w, h;
} ImageSize;
typedef struct Coord {
	int row, col;
} Coord;
void fus_dfs(const Coord* pt, const Coord* f_pt, FSU_Node* nodes, ImageSize* sz, unsigned char* mp) {
	int row = pt->row;
	int col = pt->col;
	int f_row = f_pt->row;
	int f_col = f_pt->col;
	if (row < 0 || row >= sz->h || col < 0 || col >= sz->w) return;
	int id = row * sz->w + col;
	int fid = f_row * sz->w + f_col;
	/* if (vis[id] || mp[id] == '*') return; */
	if (nodes[id].vis || mp[id] == '*') return;
	/* vis[id] = 1; */
	nodes[id].vis = 1;
	if (f_row != -1) {
		fus_union(id, fid, nodes);
	}
	int i, j;
	Coord neighbor;
	for (i = -1; i < 2; i++) {
		for (j = -1; j < 2; j++) {
			if (!i && !j) continue;
			neighbor.row = row + i;
			neighbor.col = col + j;
			fus_dfs(&neighbor, pt, nodes, sz, mp);
		}
	}
}
/*#define LOCAL*/
int main() {
#ifdef LOCAL
	const char* input_txt = "F:/zhangzhuo/debug/OJ/UVA-572.txt";
	freopen(input_txt, "r", stdin);
#endif
#define MAXN 105
	int m, n, cnt, i, j;
	/* int vis[MAXN*MAXN]; */
	unsigned char mp[MAXN*MAXN];
	FSU_Node nodes[MAXN*MAXN];
	int idx;
	while (scanf("%d%d", &m, &n) == 2 && m && n) {
		cnt = 0;
		/* memset(vis, 0, sizeof(int)*MAXN*MAXN); */
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				idx = i * n + j;
				scanf(" %c", &mp[idx]);
				/* printf("! %c !", mp[idx]); */
			}
		}
		for (i = 0; i < m*n; i++) {
			nodes[i].p = idx;
			nodes[i].rank = 1;
			nodes[i].vis = 0;
		}
		ImageSize im_sz;
		im_sz.h = m;
		im_sz.w = n;
		Coord pt;
		Coord f_pt;
		f_pt.row = -1;
		f_pt.col = -1;
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				idx = i * n + j;
				/* if (!vis[idx] && mp[idx] == '@') { */
				if (!nodes[idx].vis && mp[idx] == '@') {
					/* dfs(i, j, -1, -1); */
					pt.row = i;
					pt.col = j;
					/* fus_dfs(&pt, &f_pt, nodes, &im_sz, vis, mp); */
					fus_dfs(&pt, &f_pt, nodes, &im_sz, mp);
					cnt++;
				}
			}
		}
		printf("%d\n", cnt);
#ifdef LOCAL
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				idx = i * m + j;
				/* printf("%3d", vis[idx]); */
				printf("%c", mp[idx]);
			}
			printf("\n");
		}
		printf("\n");
#endif
	}
	return 0;
}
这里的教训是,如果在双重for循环中使用变量x、y来表示坐标,容易把2维度坐标->1维坐标的计算算错。使用row,col能减少犯错可能;
另外就是数据读取,这里改成%c,则需要过滤掉换行符\n,方法是scanf时的格式串首部添加空格:scanf(" %c", &xx)
6. 并查集,去掉了DFS
思路:遍历每个像素点,每个像素点用并查集算法合并周边8邻域中为'@'的像素点。再次遍历,统计每个'@'像素对应的等价类(root节点)的值。第三次遍历,把第二次统计的值当中cnt数大于0的累计,就是区域个数。在统计连通域个数的时候顺带把每个连通域id(像素的parent值)修改为从1开始严格单调增的序列,开启LOCAL和LOCAL_DEBUG宏可以看到。
和通常用的模板写法略有差别,比如返回root的递归终止条件,比如root初值。
不得不说,uDebug是个好东西。
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct FSU_Node {
	int p;    /* parent id */
	int rank;
} FSU_Node;
/*
get node's root id
@param x: node id
@param nodes: all nodes in map
*/
int fus_find(int x, FSU_Node* nodes) {
	if (nodes[x].p == x) {
		return x;
	}
	nodes[x].p = fus_find(nodes[x].p, nodes);
	return nodes[x].p;
}
/*
merge two node groups
@param a: a node from one node group
@param b: a node from another node group
*/
void fus_union(int a, int b, FSU_Node* nodes)
{
	int ra = fus_find(a, nodes); /* ra: root id of a */
	int rb = fus_find(b, nodes); /* rb: root id of b */
	if (ra == rb) {
		return;
	}
	if (nodes[ra].rank > nodes[rb].rank) {
		nodes[rb].p = ra;
	}
	else {
		if (nodes[ra].rank == nodes[rb].rank) {
			nodes[rb].rank++;
		}
		nodes[ra].p = rb;
	}
}
/* #define LOCAL */
/* #define LOCAL_DEBUG */
int main() {
#ifdef LOCAL
	const char* input_txt = "F:/zhangzhuo/debug/OJ/UVA-572.txt";
	freopen(input_txt, "r", stdin);
#endif
#define MAXN 105
	int m, n, cnt, i, j, k;
	int shift_x[8] = { -1, -1, -1,  0, 0,  1, 1, 1 };
	int shift_y[8] = { -1,  0,  1, -1, 1, -1, 0, 1 };
	unsigned char mp[MAXN*MAXN];
	FSU_Node nodes[MAXN*MAXN];
	int idx;
	while (scanf("%d%d", &m, &n) == 2 && m && n) {
		cnt = 0;
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				idx = i * n + j;
				scanf(" %c", &mp[idx]);
			}
		}
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				idx = i * n + j;
				nodes[idx].p = idx;
				nodes[idx].rank = 1;
			}
		}
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				idx = i * n + j;
				if (mp[idx] != '@') continue;
				for (k = 0; k < 8; k++) {
					int row = i + shift_x[k];
					int col = j + shift_y[k];
					int neighbor_idx = row * n + col;
					if (row < 0 || row >= m || col < 0 || col >= n || mp[neighbor_idx] != '@') continue;
					fus_union(idx, neighbor_idx, nodes);
				}
			}
		}
		int bowl[MAXN*MAXN] = { 0 };
		int label_cnt = 0;
		for (i = 0; i < m*n; i++) {
			if (mp[i] != '@') continue;
			int t = fus_find(i, nodes);
			nodes[i].p = t;
			if (bowl[t] == 0) {
				label_cnt++;
				bowl[t] = label_cnt;
			}
		}
		printf("%d\n", label_cnt);
#ifdef LOCAL_DEBUG
		/* print out debug info */
		for (i = 0; i < m; i++) {
			for (j = 0; j < n; j++) {
				idx = i * n + j ;
				if (mp[idx] == '@') {
					/* printf("%3d", fus_find(idx, nodes)); */
					/* printf("%3d", nodes[idx].p); */
					printf("%3d", bowl[nodes[idx].p]);
				}
				else {
					printf("%3c", '*');
				}
			}
			printf("\n");
		}
		printf("\n");
#endif
	}
	return 0;
}
UVA572 Oil Deposits DFS求解的更多相关文章
- UVa572   Oil Deposits      DFS求连通块
		技巧:遍历8个方向 ; dr <= ; dr++) ; dc <= ; dc++) || dc != ) dfs(r+dr, c+dc, id); 我的解法: #include< ... 
- HDOJ(HDU).1241 Oil Deposits(DFS)
		HDOJ(HDU).1241 Oil Deposits(DFS) [从零开始DFS(5)] 点我挑战题目 从零开始DFS HDOJ.1342 Lotto [从零开始DFS(0)] - DFS思想与框架 ... 
- Oil Deposits(dfs)
		Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission( ... 
- HDU 1241 Oil Deposits DFS(深度优先搜索) 和 BFS(广度优先搜索)
		Oil Deposits Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ... 
- HDU 1241 Oil Deposits (DFS/BFS)
		Oil Deposits Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tota ... 
- HDU-1241                Oil Deposits (DFS)
		Oil Deposits Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total ... 
- HDU_1241 Oil Deposits(DFS深搜)
		Problem Description The GeoSurvComp geologic survey company is responsible for detecting underground ... 
- UVa 572 Oil Deposits(DFS)
		Oil Deposits The GeoSurvComp geologic survey company is responsible for detecting underground oil ... 
- [POJ] 1562 Oil Deposits (DFS)
		Oil Deposits Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 16655 Accepted: 8917 Des ... 
随机推荐
- Could not find com.android.tools.build:gradle:3.3.0.
			导入新项目时报错: Error:Could not find com.android.tools.build:gradle:3.3.0. Searched in the following locat ... 
- Anaconda(一)
			一.Anaconda下载安装 Anaconda + Pycharm是知乎大佬们推荐的Python标配, Anaconda有众多版本,截至2019年11月的最新版Anaconda内置的是Python3. ... 
- 配置ogg从Oracle到PostgreSQL的同步复制json数据
			标签:goldengate postgresql oracle json 测试环境说明 Oracle:Windows 8.1 + Oracle 12.2.0.1.0 + GoldenGate 12.3 ... 
- CocosCreator TypeScript项目 (vscode 设置中文,默认调试环境设置)
			版本:2.2.1 深圳好多公司用的cocoscreator,学习一下. 这篇是如何安装,然后运行一个hello world. 一 下载 cocoscreator:https://www.cocos. ... 
- c++生成数据程序模板
			in.cpp: #include<bits/stdc++.h> #define random(a,b) rand()%(b-a+1)+a using namespace std; cons ... 
- react-navigation安卓从右到左切换视图
			百度搜了3天都没一个正确的答案,最后还是google查到的: "react-navigation": "^4.0.10", "react-navi ... 
- ASP.NET-------GridView中的字段居中不了
			在使用Grid View 控件的时候,回合一些css 放在一块使用之后你会发现 字段没有居中 你会发现该什么都不行 比如: HeaderStyle-HorizontalAlign="Cen ... 
- scrollview的优化
			针对一次加载很多格子的scrollview的优化,第一次只加载可视区域的格子,其他的用空物体占位,在每次滑动时检测需要实例化的格子,通过对象池重用第一次的格子.可以根据每行格子的数量只检测每行的第一个 ... 
- Docker下安装zookeeper(单机 & 集群)
			启动Docker后,先看一下我们有哪些选择. 有官方的当然选择官方啦~ 下载: [root@localhost admin]# docker pull zookeeper Using default ... 
- 通过excel表格分析学生成绩
			题目要求: 分析文件’课程成绩.xlsx’,至少要完成内容:分析1)每年不同班级平均成绩情况.2)不同年份总体平均成绩情况.3)不同性别学生成绩情况,并分别用合适的图表展示出三个内容的分析结果. 废话 ... 
