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

 

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

int n;
string name[101];
int score1[101],score2[101];
bool lead[101],west[101];
int essay[101];
int money[101];
int Ma,num;
int tot;

int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	cin>>n;
	for (int i=1;i<=n;i++){
		cin>>name[i]>>score1[i]>>score2[i];
		char ch;
		ch=getchar();
		while (ch!='Y' && ch!='N') ch=getchar();
		if (ch=='Y') lead[i]=1;
		ch=getchar();
		while (ch!='Y' && ch!='N') ch=getchar();
		if (ch=='Y') west[i]=1;
		cin>>essay[i];
	}
	for (int i=1;i<=n;i++){
		if (score1[i]>80 && essay[i]>=1) money[i]+=8000;
		if (score1[i]>85 && score2[i]>80) money[i]+=4000;
		if (score1[i]>90) money[i]+=2000;
		if (west[i] && score1[i]>85) money[i]+=1000;
		if (lead[i] && score2[i]>80) money[i]+=850;
		if (money[i]>Ma){
			Ma=money[i];
			num=i;
		}
		tot+=money[i];
	}
	cout<<name[num]<<endl;
	cout<<Ma<<endl<<tot<<endl;
	return 0;
}


过河
(river.pas/c/cpp)
【问题描述】
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
【输入文件】
输入文件river.in的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数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 <= 10^9。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int L;
int S,T,M;
int a[110];
int f[20000];
int v[20000];
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	scanf("%d",&L);
	scanf("%d%d%d",&S,&T,&M);
	for (int i=1;i<=M;i++) scanf("%d",&a[i]);
	sort(a+1,a+1+M);
	a[0]=0;
	a[M+1]=L;
	if (S==T){
              int ans=0;
              for (int i=1;i<=M;i++){
                  if (!(a[i] % S)) ans++;
              }
              cout<<ans<<endl;
              return 0;
    }
	for (int i=1;i<=M+1;i++){
		if (a[i]-a[i-1]>100){
                             int t=a[i]-a[i-1]-100;
                             for (int j=i;j<=M+1;j++) a[j]-=t;
        }
	}
	for (int i=1;i<=M;i++) v[a[i]]=1;
	L=a[M+1];
	memset(f,8,sizeof(f));
	f[0]=0;
	for (int i=1;i<=L;i++){
		for (int j=i-T;j<=i-S;j++){
			if (j>=0){
				f[i]=min(f[i],f[j]+v[i]);
			}
		}
	}
	int ans=f[L];
	printf("%d\n",ans);
	return 0;
}


篝火晚会
(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。

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int b[500010][3];
int a[50010],w[50010],d[50010];
int ans,n;
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("%d%d",&b[i][1],&b[i][2]);
		d[b[i][1]]++;
		d[b[i][2]]++;
	}
	for (int i=1;i<=n;i++){
		if (d[i]!=2){printf("-1\n");return 0;}
	}
	a[1]=1;
	a[2]=b[1][1];
	for (int i=3;i<=n;i++){
		if (a[i-2]!=b[a[i-1]][1]) a[i]=b[a[i-1]][1];else a[i]=b[a[i-1]][2];
	}
	if (a[n]!=b[1][2]){printf("-1\n");return 0;}
	ans=0;
	for (int i=1;i<=n;i++) w[(a[i]-i+n) % n]++;
	for (int i=0;i<n;i++) if (ans<w[i]) ans=w[i];
	memset(w,0,sizeof(w));
	for (int i=1;i<=(n+1) >> 1;i++){
		int t=a[i];a[i]=a[n-i+1];a[n-i+1]=t;
	}
	for (int i=1;i<=n;i++) w[(a[i]+n-i) % n]++;
	for (int i=0;i<n;i++) if (w[i]>ans) ans=w[i];
	printf("%d\n",n-ans);
	return 0;
}


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

 

数据有BUG,白白浪费了1h+的时间调试

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
int MM=0x7fffffff;
using namespace std;
int n;
long long shu[300];
int fu[300];
long long ans;
string s,t;
long long calc(long long a,int b,long long c){
	long long t=1LL;
	if (b=='+') t=(a+c) % MM;
	else if (b=='-') t=(a-c) % MM;
	else if (b=='*') t=(a*c) % MM;
	else if (b=='/'){
         if (c==0) c+=MM;
         t=a/c;
    }
	else if (b=='^'){
		if (a==0) t=0LL;
		else if (c==0) t=1LL;
		else for (long long i=1;i<=c;i++) t=(t*a) % MM;;
	}
	return t;
}
void val(string s,long long &t){
	long long a=0LL;
	for (size_t i=0;i<s.size();i++){
		a=a*10+(long long)(s[i]-'0');
	}
	t=a;
	t %= MM;
}
void doit(string s,long long p){
	int len=s.size();
	for (int i=0;i<len;i++){
		if (s[i]>='0' && s[i]<='9'){
			int j=i+1;
			while (s[j]>='0' && s[j]<='9') ++j;
			val(s.substr(i,j-i),shu[shu[0]+1]);
			++shu[0];
			i=j-1;
		}else
		if (s[i]=='('){
			fu[++fu[0]]='(';
			if (s[i+1]=='-'){
				s.insert(i,"0");
				len++;
			}
		}else
		if (s[i]==')'){
                       int j=fu[0];
                       while (fu[j]!='(') j--;
                       int t=fu[0];
                       for (int k=j+1;k<=t;k++){
                           shu[0]--;
                           fu[0]--;
                           shu[shu[0]]=calc(shu[shu[0]],fu[fu[0]+1],shu[shu[0]+1]);
                       }
                       --fu[0];
        }else
        if (s[i]=='+' || s[i]=='-'){
                      int j=fu[0];
                      while (fu[j]!='(') j--;
                      int t=fu[0];
                      for (int k=j+1;k<=t;k++){
                          shu[0]--;
                          fu[0]--;
                          shu[shu[0]]=calc(shu[shu[0]],fu[fu[0]+1],shu[shu[0]+1]);
                      }
                      fu[++fu[0]]=s[i];
        }else
        if (s[i]=='*' || s[i]=='/'){
                      int j=fu[0];
                      while (fu[j]=='*' || fu[j]=='/' || fu[j]=='^') j--;
                      int t=fu[0];
                      for (int k=j+1;k<=t;k++){
                          shu[0]--;
                          fu[0]--;
                          shu[shu[0]]=calc(shu[shu[0]],fu[fu[0]+1],shu[shu[0]+1]);
                      }
                      fu[++fu[0]]=s[i];
        }else
        if (s[i]=='^'){
                      int j=fu[0];
                      while (fu[j]=='^') j--;
                      int t=fu[0];
                      for (int k=j+1;k<=t;k++){
                          shu[0]--;
                          fu[0]--;
                          shu[shu[0]]=calc(shu[shu[0]],fu[fu[0]+1],shu[shu[0]+1]);
                      }
                      fu[++fu[0]]=s[i];
        }else
        if (s[i]=='a'){
                       shu[++shu[0]]=p;
        }
	}
}
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	getline(cin,s);
	s="("+s+")";
	scanf("%d\n",&n);
	for (int i=1;i<=n;i++){
		getline(cin,t);
		t="("+t+")";
		bool flag=false;
		for (long long j=0;j<5;j++){
            shu[0]=fu[0]=0;
			doit(s,j);
			ans=shu[1];
			shu[0]=fu[0]=0;
			doit(t,j);
			if (ans!=shu[1]){
				flag=true;
				break;
			}
		}
		if (!flag) putchar(i+64);
	}
	putchar('\n');
	return 0;
}