ACM第五次比赛题目及标准程序(数据结构基础)

欢迎访问XYNUOJ

问题 A: 汉诺塔(一)

时间限制: 1 Sec  内存限制: 64 MB

题目描述

在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

现在请你计算出起始有m个金片的汉诺塔金片全部移动到另外一个针上时需要移动的最少步数是多少?(由于结果太大,现在只要求你算出结果的十进制位最后六位)

输入

第一行是一个整数N表示测试数据的组数(0

输出

输出把金片起始针上全部移动到另外一个针上需要移动的最少步数的十进制表示的最后六位。

样例输入

 
   
2
1
1000

样例输出

1
69375

#include 
#include
#include
#include

using namespace std;
int nunu(int a) {
	int i;
	int m=1;
	//a = a%50000;
	/**
	  每次输入层数,输出移动步数,但如果输入的数据很大的时候,
	  我们的程序肯定会超时,这里我们经过多次测试,发现如果数据
	  大于50000的时候,会有如下规律
	  51212 =1212+50000
	  61212 =11212+50000
	  71212 =21212+50000
	  201212=1212+50000
	  就是a=a%50000+50000
	  这样程序就不会超时了
	*/
	if (a>50000)
		a = a%50000+50000;
	for(i=2; i<=a; i++) {
		/*
				汉诺塔的计算移动步数的公式是
		       f(n+1)=f(n)*2+1;
		       因为题设是保留最后六位数字,
		       所以我们的m的值为:
		       f(n+1)=(f(n)*2+1)%100000;
		    */
		m = m*2+1;
		m = m%1000000;
	}
	return m;
}
int main() {
	int a;
	int n;
	scanf("%d",&n);
	while (n--) {
		scanf("%d",&a);
		printf("%d\n",nunu(a));
	}
	return 0;
}

问题B:敌兵布阵

时间限制: 1 Sec  内存限制: 32 MB

题目描述

C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.


输入

第一行一个整数T,表示有T组数据。
每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(1<=ai<=50)。
接下来每行有一条命令,命令有4种形式:
(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
每组数据最多有40000条命令。

输出

对第i组数据,首先输出“Case i:”和回车,
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。

样例输入

1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End 

样例输出

Case 1:
6
33
59

//线段树求解 
#include
#include

int tree[50010*4];
//递归建立线段树  
void build(int left, int right, int root){
	if(left == right){
		scanf("%d", &tree[root]);
		return;
	}
	int mid = (left+right)/2;
	build(left, mid, root*2);
	build(mid+1, right, root*2+1);
	tree[root] = tree[root*2]+tree[root*2+1]; //收集子节点的结果  
}
//update这个函数就有点定制的意味了  
//本题是单点更新,所以是在区间[left,right]内使得第id数的值+add  
//如果是区间更新,可以update的参数需要将id变为L和R  
void update(int p, int add, int left, int right, int root){
	int mid = (left+right)/2;
	if(left == right){
		tree[root] += add;
		return;
	}
	if(p <= mid)
		update(p, add, left, mid, root*2);
	else
		update(p, add, mid+1, right, root*2+1);
	tree[root] = tree[root*2]+tree[root*2+1];//时刻记住维护i节点统计信息正确性  
}
//在当前区间[left, right]内查询区间[L, R]间的目标值  
//且能执行这个函数的前提是:[left,right]与[L,R]的交集非空  
//其实本函数返回的结果也是 它们交集的目标值  
int query(int L, int R, int left, int right, int root){
	int mid = (left+right)/2;
	int ans = 0;
	if(L <= left && right <= R){
		return tree[root];
	}
	if(mid >= L) ans += query(L, R, left, mid, root*2);
	if(mid+1 <= R)  ans += query(L, R, mid+1, right, root*2+1);
	return ans;
}

int main(){
	int t, n, a, b;
	int kase = 1;
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		build(1, n, 1);
		char str[10];
		printf("Case %d:\n", kase++);
		while(scanf("%s", str) != EOF){
			if(str[0] == 'E') break;
			scanf("%d%d", &a, &b);
			if(str[0] == 'A')
				update(a, b, 1, n, 1);
			else if(str[0] == 'S')
				update(a, -b, 1, n, 1);
			else
				printf("%d\n", query(a, b, 1, n, 1));
				
		}
	}
	return 0;
}//树状数组求解
#include
#include
int N, tree[50010];
//构建数 
void add(int k, int num){ //k为结点标号,Num为当前结点新加的个数 
	while(k <= N){
		tree[k] += num;
		k += k&-k;   //博客里有详解 
	}
}
void sub(int k, int num){ //k为结点标号,Num为当前结点新加的个数 
	while(k <= N){
		tree[k] -= num;
		k += k&-k;   //
	}
}
int read(int k){   //求区间1~K的和 
	int sum = 0;
	while(k){
		sum += tree[k];
		k -= k&-k;
	}
	return sum;
}
int main(){
	int m, n, ans, x, t;
	int kase = 1, flag; 
	char str[7];
	scanf("%d", &t);
	while(t--){
		memset(tree, 0, sizeof(tree));
		flag = 1;
		scanf("%d", &N);
		for(int i = 1; i <= N; i++){
			scanf("%d", &x);
			add(i, x);
		}
		while(scanf("%s", str) != EOF){
			if(str[0] == 'Q'){
				scanf("%d%d", &m, &n);
				ans = read(n)-read(m-1);
				if(flag){
					printf("Case %d:\n", kase++);
					flag = 0;
				}
				printf("%d\n", ans);
			}
			else if(str[0] == 'A'){
				scanf("%d%d", &m, &n);
				add(m, n);
			}
			else if(str[0] == 'S'){
				scanf("%d%d", &m, &n);
				sub(m, n);
			}
			else if(str[0] == 'E')
				break;
		}
	}
	return 0;
}

问题 C: 看病要排队

时间限制: 1 Sec  内存限制: 32 MB

题目描述

看病要排队这个是地球人都知道的常识。
不过经过细心的0068的观察,他发现了医院里排队还是有讲究的。0068所去的医院有三个医生(汗,这么少)同时看病。而看病的人病情有轻重,所以不能根据简单的先来先服务的原则。所以医院对每种病情规定了10种不同的优先级。级别为10的优先权最高,级别为1的优先权最低。医生在看病时,则会在他的队伍里面选择一个优先权最高的人进行诊治。如果遇到两个优先权一样的病人的话,则选择最早来排队的病人。
现在就请你帮助医院模拟这个看病过程。

输入

输入数据包含多组测试,请处理到文件结束。
每组数据第一行有一个正整数N(0
接下来有N行分别表示发生的事件。
一共有两种事件:
1:"IN A B",表示有一个拥有优先级B的病人要求医生A诊治。(0
2:"OUT A",表示医生A进行了一次诊治,诊治完毕后,病人出院。(0

输出

对于每个"OUT A"事件,请在一行里面输出被诊治人的编号ID。如果该事件时无病人需要诊治,则输出"EMPTY"。
诊治人的编号ID的定义为:在一组测试中,"IN A B"事件发生第K次时,进来的病人ID即为K。从1开始编号。

样例输入

7
IN 1 1
IN 1 2
OUT 1
OUT 2
IN 2 1
OUT 2
OUT 1
2
IN 1 1
OUT 1

样例输出

2
EMPTY
3
1
1

#include
#include
#include
#include
using namespace std;
struct node{
	int pre; //优先级 
	int id;  //id号  
	bool operator<(const node &a)const{
		if(pre == a.pre){   
			return id > a.id;
		}
		else{
			return pre < a.pre;
		}
	}
};
int main(){
	int n, a, b, sum;
	char str[10];
	node peop; //要输出的那个人 
	while(scanf("%d", &n) != EOF){
		priority_queue q[4];  //优先队列,q[i]对应第i个医生 
		sum = 1;  //序列号 
		while(n--){
			scanf("%s",str);
			if(str[0] == 'O'){
				scanf("%d", &a);
				if(q[a].empty())
					printf("EMPTY\n");
				else{
					peop = q[a].top();
					q[a].pop();
					printf("%d\n", peop.id);
				}
			}
			else if(str[0] == 'I'){
				scanf("%d%d", &a, &b);
				peop.id = sum++;
				peop.pre = b;
				q[a].push(peop);
			}
		}
	}
	return 0;
} 

问题D: Largest Rectangle in a Histogram

时间限制: 1 Sec  内存限制: 30 MB

题目描述

A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles:

Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.

输入

The input contains several test cases. Each test case describes a histogram and starts with an integer n, denoting the number of rectangles it is composed of. You may assume that 1 <= n <= 100000. Then follow n integers h1, ..., hn, where 0 <= hi <= 1000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is 1. A zero follows the input for the last test case.

输出

For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.

样例输入

5 3 4 5 6 7
1 2000
0

样例输出

16
2000

//需要求出每个矩形的高在最左边以及最右边能形成的最大距离 
#include
#define max(a,b) (a>b?a:b)
#define maxn 100000+5
typedef long long LL;  //数值较大 
LL right[maxn],left[maxn],height[maxn];

int main()
{
    LL n,cnt;
    while(~scanf("%lld",&n),n)
    {
        for(LL i=1; i<=n; ++i)
            right[i]=left[i]=i;
        for(LL i=1; i<=n; ++i)
            scanf("%lld",height+i);
        height[0]=-1,height[n+1]=-1;
        for(LL i=1; i<=n; ++i)//注意要顺序,正着来 
        {
            cnt=i-1;
            while(height[i]<=height[cnt])
            {
                left[i]=left[left[cnt]];//缩短查询时间
                cnt=left[i]-1;
            }
        }
        for(LL i=n; i>=1; --i)//注意要逆序
        {
            cnt=i+1;
            while(height[i]<=height[cnt])  //可能两个矩形之间有个较低的 
            {
                right[i]=right[right[cnt]];//缩短查询时间
                cnt=right[i]+1;
            }
        }
        LL ans=0;
        for(LL i=1; i<=n; ++i)
            ans=max(ans,(right[i]-left[i]+1)*height[i]); //求最大值 
        printf("%lld\n",ans);
    }
    return 0;
}

问题E:ACboy needs your help again!

时间限制: 1 Sec  内存限制: 30 MB

题目描述

ACboy was kidnapped!! 
he miss his mother very much and is very scare now.You can't image how dark the room he was put into is, so poor 
As a smart ACMer, you want to get ACboy out of the monster's labyrinth.But when you arrive at the gate of the maze, the monste say :
" I have heard that you are very clever, but if can't solve my problems, you will die with ACboy."
The problems of the monster is shown on the wall:
Each problem's first line is a integer N(the number of commands), and a word "FIFO" or "FILO".(you are very happy because you know "FIFO" stands for "First In First Out", and "FILO" means "First In Last Out").
and the following N lines, each line is "IN M" or "OUT", (M represent a integer).
and the answer of a problem is a passowrd of a door, so if you want to rescue ACboy, answer the problem carefully!
 

输入

The input contains multiple test cases.
The first line has one integer,represent the number oftest cases.
And the input of each subproblem are described above.

输出

For each command "OUT", you should output a integer depend on the word is "FIFO" or "FILO", or a word "None" if you don't have any integer.

样例输入

2
4 FIFO
IN 1
IN 2
OUT
OUT
4 FILO
IN 1
IN 2
OUT
OUT

样例输出

1
2
2
1

//很简单,栈,队列的模拟
//FIFO 队列 先进先出 FILO 栈 后进先出  
#include
#include
#include
#include
using namespace std;
queue q;
stack s;
int n;

void Queue(){
	int num;
	char str[20];
	while(n--){
		scanf("%s", str);
		if(str[0] == 'I'){
			scanf("%d", &num);
			q.push(num);
		}
		else if(str[0] == 'O' && !q.empty()){
			printf("%d\n", q.front());
			q.pop();
		}
		else{
			printf("None\n");
		}
	}
}
void Stack(){
	int num;
	char str[20];
	while(n--){
		scanf("%s", str);
		if(str[0] == 'I'){
			scanf("%d", &num);
			s.push(num);
		}
		else if(str[0] == 'O' && !s.empty()){
			printf("%d\n", s.top());
			s.pop();
		}
		else{
			printf("None\n");
		}
	}
}
int main(){
	int t;
	char str[10];
	scanf("%d", &t);
	while(t--){
		while(!q.empty()){  
            q.pop();  
        }  
  
        while(!s.empty()){  
            s.pop();  
        }  
		scanf("%d %s", &n, str);
		if(str[2] == 'F')
			Queue();
		else
			Stack();	
	}
	return 0;
}

问题F:小希的迷宫

时间限制: 1 Sec  内存限制: 30 MB

题目描述

上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。 

输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。 
整个文件以两个-1结尾。

输出

对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出"Yes",否则输出"No"。

样例输入

6 8  5 3  5 2  6 4
5 6  0 0

-1 -1

样例输出

Yes

//考察并查集 
//flag[i]数组标记i是否出现,FLAG标记是否有环,sum记录集合的个数  
#include  
const int N = 100005;  
int flag[N], father[N];  
void Init()  //初始化,每个节点的父节点都是自己本身,单独成为一个集合 
{  
    for(int i = 0; i <= 100000; i++)  
        flag[i] = 0, father[i] = i;  
}  
int Find(int x)  //找到根结点 
{  
    if(x != father[x])  
        father[x] = Find(father[x]);  
    return father[x];  
}  
void Merge(int a, int b)  //合并集合 
{  
    int p = Find(a);  
    int q = Find(b);  
    father[p] = q;  
}  
int main()  
{  
    int a, b;  
    while(~scanf("%d%d",&a,&b))  
    {  
  
        if(a == -1 && b == -1)  
            break;  
        Init();  
        int FLAG = 0;  //为0无环,为1有环 
        while(1)  
        {  
            if(a == 0 && b == 0)  
                break;  
            if(Find(a) == Find(b))  
                FLAG = 1;  
            Merge(a,b);  
            flag[a] = 1, flag[b] = 1;  //为1表示不是单独的集合 
            scanf("%d%d",&a,&b);  
        }  
        if(FLAG == 1)  
            printf("No\n");  
        else  
        {  
            int sum = 0;  
            for(int i = 0; i <= 100000; i++)  
                if(flag[i] && father[i] == i)  
                    sum++;   
            if(sum > 1)  //判断集合数 
                printf("No\n");  
            else  
                printf("Yes\n");  
        }  
    }  
    return 0;  
}

问题 G: 找明星

时间限制: 1 Sec  内存限制: 64 MB

题目描述

你喜欢你的偶像吗?那你能在茫茫人海中认出他来吗?现在有一群人,每个人都有一个整数编号i(0<=i<=100000000),编号可重复,还有一个空房间,现在有两种方式:一种是"ADD",表示向空房间里进m(0

输入

第一行有一个整数n(0
随后有n行;
每行可能出现如下的任意一种形式:
第一种:
一个字符串"ADD",接着是一个整数m,随后有m个i;
第二种:
一个字符串"QUERY”,接着是一个整数M,随后有M个ki;

输出

输出每次询问的结果"YES"或"NO".

样例输入

2
ADD 5 34 343 54 6 2
QUERY 4 34 54 33 66

样例输出

YES
YES
NO
NO
//http://blog.csdn.net/kay_zhyu/article/details/8851850?locationNum=2&fps=1
//离散加哈希表,理解起来有难度 
#include
#include
#include
const int N = 1000002;
#define MAX 200003;
int Head[N];
int Next[N];
int Hash[N];
void add(int &top, int num){ //int &top,必须将top引用,否则超时 
	int key = num%MAX;  
	//想向成链表,只是将链表转换成了数组来表示 
	Next[top] = Head[key];  //插入节点,那么这个节点指向的后面肯定为空 
	Head[key] = top;  //头结点连着这个插入的结点 
	Hash[top] = num;   //给这个结点一个值 
	++top;
}   //这一部分你也可以用作链表来存储 
bool search(int num){
	int key = num%MAX;
	for(int i = Head[key]; i > -1; i = Next[i]){  //依次寻找下一结点即可 
		if(Hash[i] == num){
			return true;
		}
	}
	return false;
}
int main(){
	int n, m, x;
	int top = 0;
	char str[10];
	scanf("%d", &n);
	//相当于链表中头结点为空 
	memset(Head, -1, sizeof(Head));
	while(n--){
		scanf("%s %d", str, &m);
		if(str[0] == 'A'){
			for(int i = 0; i < m; i++){
				scanf("%d", &x);
				add(top, x);
			}
		}
		else if(str[0] == 'Q'){
			for(int i = 0; i < m; i++){
				scanf("%d", &x);
				if(search(x))
					printf("YES\n");
				else
					printf("NO\n");
			}
		}
	}
	return 0;
}

问题H: 统计难题

时间限制: 2 Sec   内存限制: 128 MB

题目描述


 Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).


输入


输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
输出


对于每个提问,给出以该字符串为前缀的单词的数量.

注意:本题只有一组测试数据,处理到文件结束.


样例输入


eateggantcomacmapplebookeeaantdhjc

样例输出

21101

#include
#include
#include
#include
using namespace std;
map< string ,int > m1;
int main()
{
	string str;
	char a;
	while(1)
	{
		scanf("%c",&a);
		if(a=='\n')
		{
			scanf("%c",&a);
			str.clear();
		}
		if(a=='\n')
		break;
		str=str+a;
		m1[str]++;
	}
	while(cin>>str)
	{
		cout<



你可能感兴趣的:(赛事训练)