2020.1.16
/*吐槽:期末考试后才一天,就迎来了“洗礼”*/
A.【NOIP2005 提高组】谁拿了最多的奖学金
描述
某校的惯例是在每学期的期末考试之后发放奖学金。发放的奖学金共有五种,获取的条件各自不同:
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
思路
水题一道,开一个结构体然后进行模拟。
code
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 inline int read(){//快读 5 int ans=0,f=1; 6 char ch=getchar(); 7 while(!isdigit(ch)) f*=(ch=='-')? -1:1,ch=getchar(); 8 do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar(); 9 while(isdigit(ch)); 10 return ans*f; 11 } 12 13 struct node{ 14 int xu; 15 char name[21]; 16 int qimo; 17 int pingyi; 18 bool ganbu; 19 bool west; 20 int lunwen; 21 int total; 22 }g[101]; 23 int n,sum; 24 25 inline void pd(node &x){//判断奖学金条件,函数运行&要加 26 if(x.qimo>80 && x.lunwen>0) x.total+=8000; 27 if(x.qimo>85 && x.pingyi>80) x.total+=4000; 28 if(x.qimo>90) x.total+=2000; 29 if(x.qimo>85 && x.west==1) x.total+=1000; 30 if(x.pingyi>80 && x.ganbu==1) x.total+=850; 31 } 32 33 bool cmp(node x,node y){//排序 34 if(x.total!=y.total) return x.total>=y.total; 35 else return x.xu<y.xu; 36 } 37 38 int main(){ 39 // freopen("scholar.in","r",stdin); 40 // freopen("scholar.out","w",stdout); 41 n=read(); 42 for(int i=1;i<=n;i++){ 43 g[i].xu=i; 44 char a,b,s; 45 cin>>g[i].name; 46 g[i].qimo=read(); 47 g[i].pingyi=read(); 48 scanf("%c",&a); 49 if(a=='Y') g[i].ganbu=1; 50 else if(a=='N') g[i].ganbu=0; 51 scanf("%c",&b); 52 scanf("%c",&b);//中间有空格 53 if(b=='Y') g[i].west=1; 54 else if(b=='N') g[i].west=0; 55 g[i].lunwen=read(); 56 pd(g[i]); 57 sum+=g[i].total; 58 } 59 sort(g+1,g+1+n,cmp); 60 node x=g[1]; 61 int ans=x.total; 62 cout<<x.name; 63 printf(" %d %d",ans,sum); 64 return 0; 65 }
/***********************************************/
B.【NOIP2005 提高组】过河
描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点: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
思路
/*废话写在前面*/
很明显的一个思路就是dp,易得状态转移方程 dp[i]=min(dp[i],dp[i-j]+vis[i]) 。
但回归题目 (1 <= L <= 10^9),L这么大,暴力肯定会炸(当然暴力可以打出2、30分),但可以再发现 1 <= S <= T <= 10,1 <= M <= 100
(然而比赛时并没想到)赛后看见了大佬的题解,可以进行优化。
/*大佬的正解*/
(洛谷图)
s-----t为青蛙可以走的路径,当每隔 base=s*t 个单位 s起点与t终点重合,后面的每一个点都可以到达了,so 将长于base的短为base,进行优化。
code
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 inline int read(){ 5 int ans=0,f=1; 6 char ch=getchar(); 7 while(!isdigit(ch)) f*=(ch=='-')? -1:1,ch=getchar(); 8 do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar(); 9 while(isdigit(ch)); 10 return ans*f; 11 } 12 13 int l; 14 int s,t,m; 15 int vis[300 * 105],dp[300 * 105]; 16 int a[150],stone[150]; 17 18 int main(){ 19 memset(dp,0x7f,sizeof(dp)); 20 l=read(),s=read(),t=read(),m=read(); 21 int base=s*t; 22 for(int i=1;i<=m;i++){ 23 stone[i]=read(); 24 } 25 sort(stone+1,stone+1+m); 26 if(s==t){//special judge 27 int sum=0; 28 for(int i=1;i<=m;i++){ 29 if((stone[i]%s)==0) sum++; 30 } 31 printf("%d ",sum); 32 return 0; 33 } 34 for(int i=1;i<=m;i++){ 35 int d=stone[i]-stone[i-1]; 36 if(d>=base) d=base; 37 a[i]=a[i-1]+d; 38 vis[a[i]]=1; 39 } 40 l=a[m]+base; 41 dp[0]=0; 42 for(int i=1;i<=l;i++){ 43 for(int j=s;j<=t;j++){ 44 if(i-j>=0) { 45 if(vis[i]) dp[i]=min(dp[i],dp[i-j]+1); 46 else dp[i]=min(dp[i],dp[i-j]); 47 } 48 } 49 } 50 int ans=9999; 51 for(int i=a[m];i<=l;i++){ 52 ans=min(ans,dp[i]); 53 } 54 printf("%d",ans); 55 return 0; 56 57 }
C. 【NOIP2005 提高组】篝火晚会
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB
描述
佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有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
思路
乍一看没有一点思路,但仔细想想就会知道 如果我想和一起坐的人不想和我一起坐,悲伤,那就没办法了,直接输出-1走人;
inline bool pd(int x){ int i=g[x][0],j=g[x][1]; if( (g[i][0]==x || g[i][1]==x) && (g[j][0]==x || g[j][1]==x) ) return true; return false;
}
这道题一个巨大的坑点就是b1————bm是可以不连续的(虽然我当时想到了这个,但还是打不出来)
这时候在用dfs搞出目标解的一种情况
inline void dfs(int u,int fa) { b[++cnt]=u; if((g[u][0]==1&&g[u][1]==fa)||(g[u][1]==1&&g[u][0]==fa))return; if(g[u][0]!=fa) dfs(g[u][0],u); else dfs(g[u][1],u); }
通过目标解与1、2、3.。。。。。n的对比,找出圈转到什么情况可以有与原序的对应最多 ans
then printf(“%d”,n-ans);
方法:顺时针时,期望圈数组中第i个人,初始时应该在b[i],所以距离为(i-b[i]),为了使距离都在0~n-1之间,写为(i-b[i]+n)%n;
逆时针时,期望圈数组中第i个人,初始时应该在(n-b[i]+1)的位置【这里其实是让n作为初始圈第1个位置】同理,写为(n-b[i]+1+n)%n;
code
#include <bits/stdc++.h> using namespace std; inline int read(){ int ans=0,f=1; char ch=getchar(); while(!isdigit(ch)) f*=(ch=='-')? -1:1,ch=getchar(); do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar(); while(isdigit(ch)); return ans*f; } const int N=50005; int n; int g[N][2],b[N],cnt=0,ans; int dis[N],sum[N]; inline bool pd(int x){ int i=g[x][0],j=g[x][1]; if( (g[i][0]==x || g[i][1]==x) && (g[j][0]==x || g[j][1]==x) ) return true; return false; } inline void dfs(int u,int fa) { b[++cnt]=u; if((g[u][0]==1&&g[u][1]==fa)||(g[u][1]==1&&g[u][0]==fa))return;//如果下一个是1就说明这个圈找完了【找了一圈回去了】 if(g[u][0]!=fa) dfs(g[u][0],u); else dfs(g[u][1],u); } int main(){ // freopen("fire.in","r",stdin); // freopen("fire.out","w",stdout); n=read(); for(int i=1;i<=n;i++){ int x=read(),y=read(); g[i][0]=x,g[i][1]=y; } for(int i=1;i<=n;i++){ if(!pd(i)) { printf("-1 "); return 0; } } b[1]=1; cnt=1; dfs(g[1][0],1); for(int i=1;i<=n;i++){ dis[b[i]]=(i-b[i]+n)%n; sum[dis[b[i]]]++; } for(int i=1;i<=n;i++) ans=max(ans,sum[i]); memset(dis,0,sizeof(dis)); memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++){ dis[b[i]]=(i-(n-b[i]+1)+n)%n; sum[dis[b[i]]]++; } for(int i=1;i<=n;i++) ans=max(ans,sum[i]); printf("%d",n-ans); return 0; }
D. 【NOIP2005 提高组】等价表达式
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB
描述
明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表 达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。
这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?
这个选择题中的每个表达式都满足下面的性质:
1. 表达式只可能包含一个变量‘a’。
2. 表达式中出现的数都是正整数,而且都小于10000。
3. 表达式中可以包括四种运算‘+’(加),‘-’(减),‘’(乘),‘^’(乘幂),以及小括号‘(’,‘)’。小括号的优先级最高,其次是‘^’,然后是‘’,最 后是‘+’和‘-’。‘+’和‘-’的优先级是相同的。相同优先级的运算从左到右进行。(注意:运算符‘+’,‘-’,‘*’,‘^’以及小括号‘(’,‘)’都是英文字符)
4. 幂指数只可能是1到10之间的正整数(包括1和10)。
5. 表达式内部,头部或者尾部都可能有一些多余的空格。
下面是一些合理的表达式的例子:
((a^1) ^ 2)^3,aa+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%的数据,表达式中只可能出现两种运算符‘+’和‘-’;对于其它的数据,四种运算符‘+’,‘-’,‘*’,‘^’在表达式中都可能出现。对于全部的数据,表达式中 都可能出现小括号‘(’和‘)’。
思路
/*垃圾话写在前面*/
第一眼看到这道题毫无思路
后来观察了一下,这岂不是就是一道简单的多项式拆分(变色) 我哭,这【明明】考不进大学把,这玩意儿都不会。
后来有了一点思路:::::嗯~,只有一个变量幸运E(a),那不就可以特殊值法????!!初中时解题常用思路
但这么长的表达式咋么算啊;
/*大佬的正解*/
带入特殊值后,由于表达式的幂可能过大,直就可能爆掉,于是就要mod一个数 mod=1e9+7 hhh
另外要用上quickpow(快速幂),判断符号优先级,利用栈计算值(别忘了mod)
坑点:括号可能不匹配,
避免负值可以在摸之前加上一个mod
1 #include <bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4
5 const ll mod=1e9+7;
6 ll n,i,p,topn,topf,len,k;
7 char a[51],f[51],c[51];
8 ll num[51],t[11]={0,41,71,83,91,101};
9 ll ans[11];
10
11 inline int read(){//快读
12 int ans=0,f=1;
13 char ch=getchar();
14 while(!isdigit(ch)) f*=(ch=='-')? -1:1,ch=getchar();
15 do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
16 while(isdigit(ch));
17 return ans*f;
18 }
19
20 inline ll qpow(ll a,ll b,ll p){//快幂
21 ll s=1;
22 a%=p;
23 for(;b;b>>=1,a=(a*a)%p)
24 if(b&1) s=s*a%p;
25 return s;
26 }
27
28 inline ll level(char c){//优先级
29 if(c=='(') return 0;
30 if(c=='+' || c=='-') return 1;
31 if(c=='*' || c=='/') return 2;
32 if(c=='^') return 3;
33 return 0;
34 }
35
36 inline void work(){//计算值
37 ll x=num[topn--];
38 ll y=num[topn--];
39 char c=f[topf--];
40 if(c=='+') num[++topn]=(x+y)%mod;//算一个值就摸mod
41 else if(c=='-') num[++topn]=(y-x+mod)%mod;
42 else if(c=='*') num[++topn]=(x*y)%mod;
43 else if(c=='^') num[++topn]=qpow(y,x,mod);
44 }
45
46 inline void getnum(char a[],ll &p){
47 ll t=0;
48 while(a[p]>='0' && a[p]<='9')
49 t=(t*10+a[p++]-48)%mod;
50 num[++topn]=t;
51 }
52
53 inline void push(ll n){//将字符a用第n个数进行替换
54 num[++topn]=t[n];
55 }
56
57 inline ll solve(char a[],ll i){
58 p=topf=topn=0;
59 len=strlen(a);
60 while(p<len){
61 if(a[p]>='0' && a[p]<='9') getnum(a,p);
62 else if(a[p]==' ') p++;
63 else if(a[p]=='+' || a[p]=='-' || a[p]=='*' || a[p]=='/' || a[p]=='^'){
64 if(level(a[p]) > level(f[topf])) f[++topf]=a[p++];
65 else {
66 while(level(a[p])<=level(f[topf])) work();
67 f[++topf]=a[p++];
68 }
69 }
70 else if(a[p]=='a'){
71 push(i);
72 p++;
73 }
74 else if(a[p]=='(') f[++topf]=a[p++];
75 else if(a[p]==')') {
76 bool b=0;
77 for(int i=1;i<=topf;i++){
78 if(f[i]=='(') b=1;
79 }
80 if(!b){
81 p++;
82 continue;
83 }
84 while(f[topf]!='(') work();
85 topf--;
86 p++;
87 }
88 }
89 while(topn!=1) work();//剩下的继续
90 return num[1];
91 }
92
93 inline void getk(char a[]){//输入
94 for(int i=0;i<=50;i++){
95 a[i]='