第六章

6-1 并行程序模拟 uva210   队列(双端队列)

#include 
#include //队列容器 
#include
#include
#include //定义了有关字符判断和处理的库函数 
using namespace std;

const int maxn = 1000;

deque readyQ;
queue blockQ;
int n,quantum,c[5],var[26],ip[maxn];//n为程序数,quantum为每次最多运行时间,c[5]中存储5总语句需要执行的时间
//var[26]代表26个字母变量对应的值 
bool locked; //标识是否锁
char prog[maxn][10];//存储第maxn个程序对应的语句 

void run(int pid){
	  int q = quantum;
	  while(q > 0){
	  	char *p = prog[ip[pid]]; //ip[pid]是程序pid的当前行号。所有的程序都存在prog数组  获得pid程序的第ip[pid]语句
		switch(p[2]){//判断是哪条语句
			case '=' :
				var[p[0] - 'a'] = isdigit(p[5]) ? (p[4] - '0') * 10 + p[5] - '0' : p[4] - '0'; //变量对应的值不超过100,需要判断否有十位
				q -= c[0] ; //最多执行时间-已赋值语句执行时间 
				break;
			case 'i' : //print var 
				printf("%d: %d\n",pid+1, var[p[6] - 'a']); //p[6] 代表输出的变量; var[ p[6] - 'a']代表这个变量对应的值 
				q -= c[1];
				break;
			case 'c': //lock
				if(locked){ //如果之前已锁则加入堵塞队列 
					blockQ.push(pid); return;//加入阻塞队列后,退出循环,则未解锁之前这个程序中的语句不再执行 
				}
				locked = true;
				q -= c[2] ;
				break;
			case 'l': //unlock
				locked = false;
				if(!blockQ.empty()){ //当unlock执行完毕后,阻止队列的第一个程序进入等待队列的首部 
					int pid2 = blockQ.front();
					blockQ.pop();
					readyQ.push_front(pid2);
				}
				q -= c[3] ;
				break;
			case 'd': //end
				return;
		}
		ip[pid]++; //此时ip[pid]为程序pid中未执行语句的第一行 
}
	readyQ.push_back(pid); //一条语句结束后,判断此次时间片是否用完,如用完则该程序pid退出,否则继续执行程序pid的下一条语句 
	  
}

int main(){
	int T; //cases数
	scanf("%d",&T);
	while(T--){
		scanf("%d %d %d %d %d %d %d",&n,&c[0],&c[1],&c[2],&c[3],&c[4],&quantum);
		memset(var,0,sizeof(var));
		
		int line = 0;
		for(int i = 0; i < n; i++){ //存储所有程序的语句到prog,ip[i]记录程序i的第一条语句所在位置 
			fgets(prog[line++],maxn,stdin); //从指定的流中每次读取一行,存储在prog[line]数组中,maxn指最大字符1024 
			ip[i] = line - 1; //程序i的第一条语句所在位置 
			while(prog[line - 1][2] != 'd')//prog为二维数组 存储语句 因为语句的第三为是确定的,故判断第三位,如果是d则表示是end 结束输入 
				fgets(prog[line++],maxn,stdin);
			readyQ.push_back(i); //存储完语句,准备工作完成,将程序加入准备队列 
		}
		
		locked = false;
		while(!readyQ.empty()) {
			int pid = readyQ.front(); //获得准备队列队首程序 
			readyQ.pop_front(); //出队 
			run(pid); //执行程序 
		}
		if(T) printf("\n");
	}
	return 0;
}

 

6-2 铁轨 uva514    栈

#include
#include
using namespace std;
const int MAXN = 1010;

int n,target[MAXN];

int main() {
	while(scanf("%d",&n) && n) { //定义输入格式 如果输入的非0 则继续
		for(;;) { //不知道输入数据有多少个
			stack s; //初始化栈s
			int A = 1, B = 1;
			//每组输入数据第一个为0时结束输入
			scanf("%d",&target[1]);
			if(target[1] == 0) {
				printf("\n");   //注意输入为0时需要空行
				break; //跳出for循环
			}
			for(int i = 2; i <= n; i++) { //从1开始
				scanf("%d", &target[i]);
			}
			int ok = 1;
			while(B <= n) {
				if(A == target[B]) {
					A++;
					B++;
				} else if(!s.empty() && s.top() == target[B]) {
					s.pop();
					B++;
				} else if(A <=n) s.push(A++);
				else {
					ok = 0;
					break;
				}
			}
			printf("%s\n",ok ? "Yes" : "No");


		}
	}
	return 0;
}

 

6-3 矩阵链乘 uva442     栈

 

#include 
#include
#include
#include
using namespace std;
//由Expression = Matrix | "(" Expression Expression ")"可以得到左括号和右括号是相匹配的,并且括号对数比矩阵数多一
//故可以遇到左括号忽略,遇到矩阵入栈,遇到右括号出栈。 如果格式定义为AB则错误 
struct Matrix {
	int a,b; //成员变量 
	Matrix(int a = 0, int b = 0) : a(a),b(b){} //构造函数 先将a和b初始化为0,若主函数里的调用函数中参数a和b不为零,则a=a,b=b; 
} m[26]; //m[26] 结构体数组 

stack s;

int main(){
	int n;
	cin >> n;
	for(int i = 0; i < n; i++){
		string name;
		cin >> name;
		int k = name[0] - 'A'; //string 类型(name)没有减号运算符,字符类型有! 
		cin >> m[k].a >> m[k].b;
	}
	
	string expr;
	while(cin >> expr){
		int len = expr.length();
		bool error = false;
		int ans = 0;
		for(int i = 0; i < len; i++){
			if(isalpha(expr[i])) s.push(m[expr[i] - 'A']);
			else if(expr[i] == ')'){
				Matrix m2 = s.top(); s.pop();
				Matrix m1 = s.top(); s.pop();
				if(m1.b != m2.a) { error = true; break;
				}else{
					ans += m1.a * m1.b * m2.b;
					s.push(Matrix(m1.a,m2.b));
				}
			}
		}
		if(error) printf("error\n"); else printf("%d\n",ans);
	}
	return 0;
}

 

6-4 破损的键盘 uva11988    不太懂,需要回顾

链表 有指针域和数据域

也可用两个数组替代指针:next数组表示结点的地址,data数组表示数据的地址 ||||| next[0]中存储的值是下一个结点的地址,同时也是本个结点的数据地址(另一个数组存储数据) 

 

#include 
#include
const int maxn = 100000 + 5;
int last, cur, next[maxn]; //光标位于cur字符之后
char s[maxn] ;
/*
采用链表
将输入的每个字符都存到字符串列表s[1~n]
用next[i]表示当前显示屏中s[i]右边的字符编号(即在s中的下标)
next[0~n-1] next[0]表示s[1]
cur表示光标位置 cur=1表示当前光标在s[1]的右边 cur=0表示当前光标在 虚拟字符s[0]右边 
*/ 
s[next[i]] s[next[next[i]]] s[next[next[next[i]]]]
int main(){
	while(scanf("%s",s+1) == 1) { //从s[1]开始存储字符 
		int n = strlen(s+1) ; //输入保存在s[1],s[2]...中
		last = cur = 0;
		next[0] = 0;
		
		for(int i = 1; i <= n;i++){
			char ch = s[i];
			if(ch == '[') cur = 0;
			else if(ch == ']') cur = last;
			else{
				next[i] = next[cur];
				next[cur] = i; //next中存储光标位置
				if(cur == last)  last = i;//一般来说当前光标和最后光标相同,如果cur移动到首部,则最后光标位置不变
				cur = i; //移动光标 
			}
		}
		for(int  i = next[0]; i != 0; i = next[i])
			printf("%c",s[i]);
		printf("\n");
	}
	return 0;
}

6-6 小球下落 uva679  二叉树结点状态的改变

分析:由题意知该二叉树为满二叉树(所有结点从上到下从左到右编号为1、2、3....2^D-1)。每个结点初始状态为关闭。有很多个小球,小球在某结点处如果结点状态为关闭,往左下走,同时改变结点状态,否则往右下走,同时改变结点状态。问有D层二叉树,第i个小球会落到哪个叶子结点。

思路:

初始化结点状态数组为0,

for循环让i个小球下落,每次下落需先改变当前结点状态在下落(如果先下落再改变之前的结点状态的话会比较麻烦),直到下落的结点出界。

最后一次循环下落的值即为所求的叶子编号。

#include
#include
const int maxd = 20;//D<=20
int s[1<n) break;  //这样判断更具有一般性
				}
			}
			printf("%d\n",k/2); //“出界”之前的叶子编号
		}

	}
	return 0;
}

但是!!!这样的实现有个很大的缺点,运算量太大。提交后会出现超时提醒。

改进:每个小球都会落在根节点上,因此前两个小球必然是一个左子树,一个在右子树。一般地,只需看小球编号的奇偶性,就能知道它最终在哪颗子树中,对于拿些落入根结点左子树的小球来说,只需知道该小球是第几个落在根的左子树里的,就可以知道它下一步往左还是往右。以此类推,直到小球落到叶子上。

如果使用题目中给出的编号I,则当I是奇数时,它是往左走的第(I+1)/2个小球,如果I是偶数,它是往右走的第I/2个小球。这样可以直接模拟最后一个小球的路线。

I是奇数往左走是因为初始结点是关闭的,要改变结点状态然后往左子结点走。

I是偶数时结点状态是开的,要改变结点状态然后往右子结点走。

然后每个结点都可看作是根结点。

#include
#include

int main(){
	int l;
	while(scanf("%d",&l) && l!=-1){
		for(int i = 0; i < l; i++){
			int D,I;
			scanf("%d %d",&D,&I);
			int k = 1;
			for(int i=1; i < D;i++){ //模拟最后一个小球路线 到D-1层即可 
				if(I%2){ k = k*2; I = (I+1)/2;} //I%2的可能值0、1   
				else {k = k*2+1;I/=2;}
			}
		printf("%d\n",k);			
		}
	}
	return 0;
}

 

 

6-7 二叉树的层次遍历 uva122     构造树,bfs,添加结点

#include
#include
#include
#include
#include
using namespace std;

const int maxn = 256 + 10;

//定义结点结构
struct Node{
	bool have_value;//是否有值 (因为由结点构造树是不按顺序的,有可能从根到某个叶结点的路径上有的结点没有在输入中给出,或者给出超过了一次)
	int v; //保存结点的值
	Node* left,*right;//左右子结点
	//结构体的构造函数 
	Node():have_value(false),left(NULL),right(NULL){}
}; 

Node* root; //根结点

//申请新结点 
Node* newnode(){
	return new Node();
} 

//添加新结点 
bool failed; 
void addnode(int v,char *s){
	int n = strlen(s);
	Node* u = root;//指向根结点的指针
	for(int i = 0; i < n; i++){
		if(s[i] == 'L'){
			if(u -> left == NULL) u->left = newnode();
			u = u->left;
		}else if(s[i] == 'R'){
			if(u -> right == NULL) u->right = newnode();
			u = u->right;
		}
	}
	if(u->have_value) failed = true; //如果此结点之前已有值则创建结点失败 
	u->v = v;
	u->have_value = true; 
} 

//释放二叉树 清内存
void remove_tree(Node* u){
	if(u == NULL) return;
	remove_tree(u->left);
	remove_tree(u->right);
	delete u;
} 

//读入
char s[maxn];
bool read_input(){
	failed = false;
	remove_tree(root); //创建树之前先清空
	root = newnode(); //根结点
	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& ans){
	queue q; //q队列存放结点
	ans.clear();
	q.push(root);
	while(!q.empty()){
		Node* u = q.front(); q.pop();
		if(!u->have_value) return false;
		ans.push_back(u->v); //记录本结点值 
		if(u->left != NULL) q.push(u->left);//左孩子结点入队 
		if(u->right != NULL) q.push(u->right); //右孩子结点入队 
	} 
	return true; 
} 

int main(){
	vector ans;
	while(read_input()){
		if(!bfs(ans)) failed = 1;
		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;
}

 

你可能感兴趣的:(算法竞赛入门)