【复习】noip2005

这套题我折腾了太久,实在没有达到比赛要求。。反省面壁。



(提高组 三小时完成)

 

谁拿了最多奖学金

(scholar.pas/c/cpp)

【问题描述】


   
某校的惯例是在每学期的期末考试之后发放奖学金。发放的奖学金共有五种,获取的条件各自不同:


1)院士奖学金,每人8000元,期末平均成绩高于80分(>80),并且在本学期内发表1篇或1篇以上论文的学生均可获得;

2)五四奖学金,每人4000元,期末平均成绩高于85分(>85),并且班级评议成绩高于80分(>80)的学生均可获得;

3)成绩优秀奖,每人2000元,期末平均成绩高于90分(>90)的学生均可获得;
4)西部奖学金,每人1000元,期末平均成绩高于85分(>85)的西部省份学生均可获得;
5)班级贡献奖,每人850元,班级评议成绩高于80分(>80)的学生干部均可获得;

 

只要符合条件就可以得奖,每项奖学金的获奖人数没有限制,每名学生也可以同时获得多项奖学金。例如姚林的期末平均成绩是87分,班级评议成绩82分,同时他还是一位学生干部,那么他可以同时获得五四奖学金和班级贡献奖,奖金总数是4850元。
   

现在给出若干学生的相关数据,请计算哪些同学获得的奖金总数最高(假设总有同学能满足获得奖学金的条件)。

【输入文件】

    输入文件scholar.in的第一行是一个整数N(1 <= N <= 100),表示学生的总数。接下来的N行每行是一位学生的数据,从左向右依次是姓名,期末平均成绩,班级评议成绩,是否是学生干部,是否是西部省份学生,以及发表的论文数。姓名是由大小写英文字母组成的长度不超过20的字符串(不含空格);期末平均成绩和班级评议成绩都是0到100之间的整数(包括0和100);是否是学生干部和是否是西部省份学生分别用一个字符表示,Y表示是,N表示不是;发表的论文数是0到10的整数(包括0和10)。每两个相邻数据项之间用一个空格分隔。

【输出文件】

输出文件scholar.out包括三行,第一行是获得最多奖金的学生的姓名,第二行是这名学生获得的奖金总数。如果有两位或两位以上的学生获得的奖金最多,输出他们之中在输入文件中出现最早的学生的姓名。第三行是这N个学生获得的奖学金的总数。

【样例输入】
4
YaoLin 87 82 Y N 0
ChenRuiyi 88 78 N Y 1
LiXin 92 88 N N 0
ZhangQin 83 87 Y N 1

【样例输出】
ChenRuiyi
9000
28700

过河

(river.pas/c/cpp)

 

【问题描述】


   
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。

   
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

【输入文件】


   
输入文件river.in的第一行有一个正整数L(1<= L <= 109),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <=100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

【输出文件】

输出文件river.out只包括一个整数,表示青蛙过河最少需要踩到的石子数。

【样例输入】

10
2 3 5
2 3 5 6 7

【样例输出】

2

【数据规模】
对于30%的数据,L<= 10000;
对于全部的数据,L <= 109。


篝火晚会

(fire.pas/c/cpp)

【问题描述】

 

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有n个同学,编号从1到n。一开始,同学们按照1,2,……,n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。

佳佳可向同学们下达命令,每一个命令的形式如下:
(b1, b2,... bm -1, bm)


   
这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1,b2,…… bm –1,bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。

执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

 

【输入文件】

输入文件fire.in的第一行是一个整数n(3 <= n <= 50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。

【输出文件】

输出文件fire.out包括一行,这一行只包含一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出-1。

【样例输入】

4
3 4
4 3
1 2
1 2
【样例输出】
2
【数据规模】
对于30%的数据,n<= 1000;
对于全部的数据,n <= 50000。

等价表达式

(equal.pas/c/cpp)

 

【问题描述】

明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。
这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?

这个选择题中的每个表达式都满足下面的性质:
1. 表达式只可能包含一个变量‘a’。
2. 表达式中出现的数都是正整数,而且都小于10000。
3. 表达式中可以包括四种运算‘+’(加),‘-’(减),‘*’(乘),‘^’(乘幂),以及小括号‘(’,‘)’。小括号的优先级最高,其次是‘^’,然后是‘*’,最后是‘+’和‘-’。‘+’和‘-’的优先级是相同的。相同优先级的运算从左到右进行。(注意:运算符‘+’,‘-’,‘*’,‘^’以及小括号‘(’,‘)’都是英文字符)
4. 幂指数只可能是1到10之间的正整数(包括1和10)。
5. 表达式内部,头部或者尾部都可能有一些多余的空格。

下面是一些合理的表达式的例子:

((a^1) ^ 2)^3,a*a+a-a,((a+a)),9999+(a-a)*a,1 + (a -1)^3,1^10^9……

【输入文件】

   
输入文件equal.in的第一行给出的是题干中的表达式。第二行是一个整数n(2 <= n <= 26),表示选项的个数。后面n行,每行包括一个选项中的表达式。这n个选项的标号分别是A,B,C,D……

   
输入中的表达式的长度都不超过50个字符,而且保证选项中总有表达式和题干中的表达式是等价的。
【输出文件】
   
输出文件equal.out包括一行,这一行包括一系列选项的标号,表示哪些选项是和题干中的表达式等价的。选项的标号按照字母顺序排列,而且之间没有空格。


【样例输入】

( a + 1) ^2
3
(a-1)^2+4*a
a + 1+ a
a^2 + 2 * a * 1 + 1^2 + 10 -10 +a -a

【样例输出】
AC

【数据规模】

对于30%的数据,表达式中只可能出现两种运算符‘+’和‘-’;
对于其它的数据,四种运算符‘+’,‘-’,‘*’,‘^’在表达式中都可能出现。
对于全部的数据,表达式中都可能出现小括号‘(’和‘)’。


#include <cstring>
#include <string>
#include <cstdio>
#include <iostream>
using std::cout;

long getint()
{
	long rs=0;bool sgn=1;char tmp;
	do tmp = getchar();
	while (!isdigit(tmp)&&tmp-'-');
	if (tmp=='-'){sgn=0;tmp=getchar();}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;
}

char name[120][50];

int main()
{
	freopen("scholar.in","r",stdin);
	freopen("scholar.out","w",stdout);
	long n = getint();
	long long maxscho = 0;
	long ans = 0;
	long long total = 0;
	long final,decide,essay;
	bool west,manager;
	for (long i=1;i<n+1;i++)
	{
		scanf("%s",name+i);
		final = getint();
		decide = getint();
		char yn;
		do yn = getchar();
		while (yn-'Y'&&yn-'N');
		manager = yn=='Y';
		do yn = getchar();
		while (yn-'Y'&&yn-'N');
		west = yn=='Y';
		essay = getint();

		long long scholar = 0;
		if (final>80 && essay>0)
			scholar += 8000;
		if (final>85 && decide>80)
			scholar += 4000;
		if (final>90)
			scholar += 2000;
		if (final>85 && west)
			scholar += 1000;
		if (decide>80 && manager)
			scholar += 850;
		if (scholar > maxscho)
		{
			maxscho = scholar;
			ans = i;
		}
		total += scholar;
	}
//	printf("%s\n%ld\n%ld",name[ans],maxscho,total);
	cout << name[ans] << "\n" << maxscho << "\n" << total;
	return 0;
}

篝火晚会有必要写下题解。

(部分参考http://hi.baidu.com/qyjubriskxp/blog/item/282b17cc37b2aa34b700c856.html)


这个要扯到群论里的一个定理。

一个非单位元n阶置换总是可以唯一地表示为若干互不相交k-循环置换的积。

(口水话来说就是就是最少用位置不同的总个数的代价置换(口水过头了别怪我))

比较拗口,要用到高等数学的知识。。汗。。提到这个总是让我觉得自己浪费了太多光阴,导致什么都不懂。

其实,比较容易理解。。最优方案是这样得到的。比如说:

如果a占到了b的位置,b占到了c的位置,c占到了d的位置,d占到了a的位置。则:

1 2 3 4    ×    1 2 3 4   = 1 2 3 4      即先交换1 2,再交换1 3最后导致1 3 2的交换,而代价却是4,不如直接

2 1 3 4          1 3 2 4          3 1 2 4      交换1 3 2的代价3划算


接下来要用到一个技巧。因为如果按照以上思想,会有2n种构造方法,每种构造方法要用O(n)的时间来计算位置不同的总个数,时间复杂度是O(n^2)

这要来考虑,我们只管位置相同的,剩下的就都是位置不同的,什么时候位置相同可以用O(1)可以计算出来,类似于有一个原始序列,我们把它平移,多种平移的方法,每种平移方法会对应多个位置变成相同的。我们只需要用桶排那种办法记录下来,然后再枚举各个平移量,就可以得到最优解。(注意,平移的时候记得要取余)

#include <cstdio>
#include <string>

long getint()
{
	long rs=0;bool sgn=1;char tmp;
	do tmp = getchar();
	while (!isdigit(tmp)&&tmp-'-');
	if (tmp=='-'){sgn=0;tmp=getchar();}
	do rs=(rs<<3)+(rs<<1)+tmp-'0';
	while (isdigit(tmp=getchar()));
	return sgn?rs:-rs;
}

long n;
long seq[50010];
long nxt[50010];
long pre[50010];
long l[50010];
long r[50010];
long f1[100010];
long f2[100010];
long maxscho = 0;
bool used[50010];

int main()
{
	freopen("fire.in","r",stdin);
	freopen("fire.out","w",stdout);

	n = getint();
	for (long i=1;i<n+1;i++)
	{
		l[i] = getint();
		r[i] = getint();
	}
	nxt[1] = l[1];
	pre[l[1]] = 1;
	long i = l[1];
	do
	{
		long nn = pre[i]==l[i]?r[i]:l[i];
		nxt[i] = nn;
		pre[nn] = i;
		used[i] = true;
		i = nn;
		if (used[i])
		{
			printf("-1");
			return 0;
		}
	}while (i != 1);
	
	long u = 1;
	for (long i=0;i<n;i++)
	{
		seq[i] = u-1;
		u = nxt[u];
	}
	for (long i=0;i<n;i++)
		f1[(seq[i]-i+n)%n] ++;
	for (long i=0;i<2*n+1;i++)
		maxscho = maxscho<f1[i]?f1[i]:maxscho;
		
	u = 1;
	for (long i=n-1;i>0;i--)
	{
		seq[i] = u-1;
		u = nxt[u];
	}
	for (long i=0;i<n;i++)
		f2[(seq[i]-i+n)%n] ++;
	for (long i=0;i<2*n+1;i++)
		maxscho = maxscho<f2[i]?f2[i]:maxscho;
  

	printf("%ld",n-maxscho);
	return 0;
}

过河其实优化也很简单的,但是还是得基本功很扎实,才能够想到这样的方法。

一开始WA80。因为,把能够压缩和不能够压缩的混了。pos+=(sto[i]-sto[i-1])%t。这样是错误的。

因为如果要压缩,只能压缩(n-1)个t,不能压缩n个t,就是说在以上还要加上一个t。

压缩的目的是让空条变短,但是不能完全让他消失,因为要保持原来的性质。

#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>

using std::min;

long f[10000000];
char stone[10000000];
long sto[110];

int main()
{
	freopen("river.in","r",stdin);
	freopen("river.out","w",stdout);
	
	memset(f,0x7f,sizeof f);
	long l,s,t,m;
	scanf("%ld%ld%ld%ld",&l,&s,&t,&m);
	for (long i=1;i<m+1;i++)
	{
        long tmp;
		scanf("%ld",&sto[i]);
    }
	sto[m+1] = l;
	std::sort(sto,sto+m+2);

	long pos = 0;
	for (long i=1;i<m+2;i++)
	{
		if (sto[i]-sto[i-1]>t)
			pos += (sto[i]-sto[i-1])%t+t;
		else
			pos += (sto[i]-sto[i-1]);
		stone[pos] = true;
	}
	sto[pos] = false;
	l = pos;
    for (long i=s;i<t+1;i++)
        f[i] = stone[i];
	for (long i=t+1;i<l+t;i++)
	{
		for (long j=i-t;j<=i-s;j++)
		{
			f[i] = min(f[i],f[j]+stone[i]);
		}
	}
	long ans = 0x7f7f7f7f;
	for (long i=l;i<l+t;i++)
		ans = min(ans,f[i]);
	printf("%ld",ans);
	return 0;
}

只有这道题做得稍微略微。。顺利。。一点点。

1、WA30。因为检验的范围小了[-10,10]。

2、WA90。因为最后一组爆了,数据太大

3、WA70。改成了取余,说真的,不知道为什么取余不行。

4、AC。把long改成了long long。代价是慢了很多很多。


有些需要注意的地方。

1、遇到右括号时,把括号里边的东西处理完,要记得把左括号也弹出来。

2、注意空的优先级,是最低的。同样,左括号也是最低的。


#include <cstdio>

#define level(a) ((a)==0||(a)=='('?0:(a)=='+'||(a)=='-'?1:(a)=='*'?2:3)

bool IsNum(char tmp)
{
	return tmp<='9'&&tmp>='0';
}

char _basic[60];
char basic[60];
char answer[30][60];
long n;

bool fail[60];
char stack1[60];
long long stack2[60];
long top1 = 2;
long top2 = 2;
long long rep = 0;

inline long long power(long long a,long long b)
{
	long long rs = 1;
	long long tmp = a;
	while (b)
	{
		if (b&1){rs = rs*tmp;}
		tmp *= tmp;
		b >>= 1;
	}
	return rs;
}
inline long long calc(long long a,long long b,char c)
{
	if (c == '+')
		return a+b;
	if (c == '-')
		return a-b;
	if (c == '*')
		return a*b;
	if (c == '^')
		return power(a,b);
}

long long work(char* str)
{
	top1 = top2 = 2;
	long i = 0;
	while (str[++i])
	{
		if (str[i] == ' ') continue;
		if (IsNum(str[i]))
		{
			long long rs = 0;
			long j = i-1;
			while (str[++j])
			{
				if (!IsNum(str[j]))
					break;
				rs = (rs<<3)+(rs<<1)+str[j]-'0';
			}
			stack2[++top2] = rs;
			i = j-1;
		}
		else if (str[i]=='a')
			stack2[++top2] = rep;
		else if (str[i]=='(')
			stack1[++top1] = '(';
		else if (str[i]==')')
		{
			while (stack1[top1] != '(')
			{
				long long num1 = stack2[top2--];
				long long num2 = stack2[top2--];
				if (top2 < 2) top2 = 2;
				char opt = stack1[top1--];
				long long num = calc(num2,num1,opt);
				stack2[++top2] = num;
			}
			top1 --;
		}
		else if (level(str[i]) > level(stack1[top1]))
			stack1[++top1] = str[i];
		else
		{
			while (level(str[i]) <= level(stack1[top1]) && stack1[top1])
			{
				long long num1 = stack2[top2--];
				long long num2 = stack2[top2--];
				if (top2 < 2) top2 = 2;
				char opt = stack1[top1--];
				long long num = calc(num2,num1,opt);
				stack2[++top2] = num;
			}
			stack1[++top1] = str[i];
		}
	}
	while (top1 > 2)
	{
		long long num1 = stack2[top2--];
		long long num2 = stack2[top2--];
		if (top2 < 2) top2 = 2;
		char opt = stack1[top1--];
		long long num = calc(num2,num1,opt);
		stack2[++top2] = num;
	}
	return stack2[3];
}

int main()
{
	freopen("equal.in","r",stdin);
	freopen("equal.out","w",stdout);

	scanf("%[^\n]",_basic+1);
	long len = 0;
	long _len = 0;

	while (_basic[++_len])
	{
		if (_basic[_len] == '-'
				&&(basic[len] == '('||len==0))
		{
			basic[++len] = '(';
			basic[++len] = '0';
			basic[++len] = '-';
			long j = _len + 1;
			while (IsNum(_basic[j]) || _basic[j]==' ')
			{
				if (_basic[j] == ' ')
				{
					j ++;
					continue;
				}
				basic[++len] = _basic[j];
				j ++;
			}
			_len = j-1; 
			basic[++len] = ')';
		}
		else if (_basic[_len] != ' ')
			basic[++len] = _basic[_len];
	}

	scanf("%ld",&n);
	for (long i=1;i<n+1;i++)
	{
        getchar();
		scanf("%[^\n]",_basic+1);
		long len = 0;
		long _len = 0;

		while (_basic[++_len])
		{
			if (_basic[_len] == '-'
					&&(answer[i][len] == '('||len==0))
			{
				answer[i][++len] = '(';
				answer[i][++len] = '0';
				answer[i][++len] = '-';
				long j = _len + 1;
				while (IsNum(_basic[j]) || _basic[j]==' ')
				{
					if (_basic[j] == ' ')
					{
						j ++;
						continue;
					}
					answer[i][++len] = _basic[j];
					j ++;
				}
				_len = j-1; 
				answer[i][++len] = ')';
			}
			else if (_basic[_len] != ' ')
				answer[i][++len] = _basic[_len];
		}
	}

	for (long i=-100000;i<100001;i+=13)
	{
		rep = i;
		long long a = work(basic);
		for (long j=1;j<n+1;j++)
		{
			if (!fail[j])
			{
				long long b = work(answer[j]);
				if (a != b)
				{
					fail[j] = true;
					break;
				}
			}
		}
	}

	for (long j=1;j<n+1;j++)
		if (!fail[j])
			printf("%c",'A'+j-1);
	return 0;
}


谁拿了最多奖学金 • 某校的惯例是在每学期期末考试之后发放奖学金发放奖学金共有五种获取件各自不同: • 1) 院士奖学金,每人8000元,期末平均成绩高于80分( >80),并且在本学期内发表1篇或1篇以上论文的学生均可获得; • 2) 五四奖学金,每人4000元,期末平均成绩高于85分( >85),并且班级评议成绩高于80分(>80)的学生均可获得; • 3) 成绩优秀奖,每人2000元,期末平均成绩高于90分( >90)的学生均可获得; 4) 西部奖学金,每人1000元,期末平均成绩高于85分( >85)的西部省份学生均可获得;5) 班级贡献奖,每人850元,班级评议成绩高于80分(>80)的学生干部均可获得; • 只要符合件就可以得奖,每项奖学金的获奖人数没有限制,每名学生也可以同时获得多项奖学金。 • 例如姚林的期末平均成绩是87分,班级评议成绩82分,同时他还是一位学生干部,那么他可以同时获得五四奖学金 和班级贡献奖,奖金总数是4850元。现在给出若干学生的相关数据,请计算哪些同学获得的奖金总数最高(假设总 有同学能满足获得奖学金件)。 Input 输入的第一行是一个整数N(1 <= N <= 100),表示学生的总数。 • 接下来的N行每行是一位学生的数据,从左向右依次是姓名,期末平均成绩,班级评议成绩,是否是学生干部,是 否是西部省份学生,以及发表的论文数。姓名是由大小写 英文字母组成的长度不超过20的字符串(不含空格);期末平均成绩和班级评议成绩都是0到100之间的整数(包括0和100);是否是学生干部和是否是西部省份学生分别用一个字符表示,Y表示是,N表示不是;发表的论文数是0 到10的整数(包括0和10)。每两个相邻数据项之间用一个空格分隔。 Output 输出包括三行,第一行是获得最多奖金的学生的姓名,第二行是这名学生获得的奖金总数。如果有两位或两位以上 的学生获得的奖金最多,输出他们之中在输入文件中出现 最早的学生的姓名。第三行是这N个学生获得的奖学金的总数。 • • Sample Input Sample Output 4 YaoLin 87 82 Y N 0 ChenRuiyi ChenRuiyi 88 78 N Y 1 9000 LiXin 92 88 N N 0 28700 ZhangQin 83 87 Y N 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值