考试总结:
1. 显然第一道送分题,我差一点直接打表来对拍了,后来发现我们的pdf有问题不能复制,并且我没有YJQ大神那样足够的时间每道题都对拍,就只能试过样例就遁,(其实这种密码我玩过,密码学入门密码,当时好像叫凯撒密码233);对了,ASCII的掌握也很重要,我之前一直以为大写在小写后面啧。(之间漏掉过大于以后减的步骤,这种简单题还是做少了居然耗了30min,以后好好检查争取一次过);
2. 这道题我拿到的第一反应就是贪心,马上想了一个贪心规则但自己总觉得是错的,花了很长的时间举反例举不出来,后来和第三道题换着做都没做完快疯了,然后发现我无法证伪,但是它本来就是正确的啊…所以说以后要多从正面,特别是反证的方面思考问题;最后写出来以后输出不对,才发现是前面不包括自己,当时懵比因为那样的话我的方法就错了,然后突然发现两边都乘以一个lef就一样了,还是一种很巧妙的思维转化吧,然后得出了正确的算法,当然由于畏难我没有写高精度,后来发现其实很简单,就几行,所以不要畏难,熟练掌握模板(对了,还有个技巧是sort排序可以各种写cmp,对于排序的灵活性非常有用;
3. 本来打算用DP啊背包啊记忆啊什么的,所以还是基本概念掌握不清晰,很明显这些算法不能用;后来发现就求min1,min2的时间复杂度最高,然后一个快排就可以解决问题(当然,要写很多个if很麻烦),最后还是决定拿70分走,然后发现太难调了,临交卷还没调出来orz.)
4. 总的来说,这套题考得排名不错,但我认为不是很理想,第二题证伪的时间太长,没有把握好时间,没有想到反证,高精度没敢写,最后一道题没有先检查再调试,导致很多一眼看出来的错调了很久,要站在逻辑的高度上。代码实现能力有待提高,60多行的程序应该写得心平气和思路清晰才对;第二三题都还是很有价值(第二题的证明,第三题的错orz);
解题报告:
一. Vigenère密码
1.题意:通过按位的方式把暗文按照密钥翻译成明文,每个明文和密钥对应一个暗文;数据范围:密钥100,暗文1000;
2.注意:
1).是把暗文翻译成明文,害得我总觉得样例有问题,一定要仔细读题;
2).题目要求明文大写暗文保持大写,密钥大小写无关紧要;
3.分析:这道题的第二个注意揭示了,这是一道需要很多个if的题,只要考虑清楚,这道题就没问题;思路就是用ASCII码的运算,把密钥当做加法,a加0,b加1…
4..代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int lena,lenb;
char a[105],b[1005];//meaning
int main()
{
freopen("vigenere.in","r",stdin);
freopen("vigenere.out","w",stdout);
scanf("%s",a);getchar();scanf("%s",b);lena=strlen(a);lenb=strlen(b);
for(int i=0;i<lenb;i++){
int tmp=b[i];tmp-=a[i%lena];if(a[i%lena]<=‘Z‘)tmp+=‘A‘;else tmp+=‘a‘;
if(b[i]<=‘Z‘&&tmp<‘A‘)tmp+=26;//ditails;
else if(b[i]>‘Z‘&&tmp<‘a‘)tmp+=26;
b[i]=tmp;
}
printf("%s",b);
return 0;
}//对了,用scanf一定记得注意换行符;
二. 国王游戏
- 题意:给定一个第一位不能动的数列,后面的得分为之前所有的左元素的乘积除以自己的右元素,使最大得分最小;
数据范围:对于20%的数据,有1≤n≤10,0 < a、b <8;
对于40%的数据,有1≤n≤20,0 < a、b < 8;
对于60%的数据,有1≤n≤100;
对于60%的数据,保证答案不超过109;
对于100%的数据,有1 ≤n ≤1,000,0 < a、b < 10000。
- 注意:
1) 是前面的所有人,不包括自己!
2) <=10000的 1000次方应该是最多4000位,但如果是五位数就是5000了;
- 分析:要最大最小就对r*l从小到大排序,然后直接运算就行了;
算法证明:
1).基本思路:r*=l之后,本题就可以看作最左边包括自己的积除以r*l,左边的乘积一定是单增的,则除数也要单增,且单独看最后一个,被除数肯定是最大的,那么除数也要尽量大;
2)正确性证明:当时决定采用这个算法时已经没有时间了,其实即使不r*=l左边也是单增,但是r*=l和直接排r肯定是不一样的,所以这里还涉及到反证的思想;
①.不相等时:不相等时,前后交换,后面的那个数和之前在后面的那个数面对的被除数是一样的,也就是说当后面的元素确定后交换不影响后一个位置的除数,然后被除数一样,交换后除数反而小了,所以得到的数肯定比之前相同除数被除数小的情况大,也比之前相同被除数除数大的情况大,最大值即使不会变大,也不可能变小;
②。相等时:永远要记住讨论相等,交换后,后一个数除数没变被除数没变,把l大的放在后面的话,前面的数被除数小了除数没变,至少不会更大,但是前面被除数至少不会更大,除数又相等,所以至少不会让最大值变化(因为后一个要么相等要么更大),所以相等时可以随意;
(啊证明这种东西感觉对自己的逻辑学很有帮助);
4. 代码:#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int maxn[5005],n,tmp[5005],ans[5005];
struct per
{
int l,r;
}a[1005];//条件
bool cmp(per x,per y)
{
if(x.r*x.l<y.r*y.l)return true;
return false;
}
void chengf(int u)
{
memset(tmp,0,sizeof(tmp));//高乘单!
for(int i=1;i<=5003;i++){
tmp[i]+=maxn[i]*a[u].l;tmp[i+1]+=tmp[i]/10;tmp[i]%=10;
}
for(int i=1;i<=5004;i++)maxn[i]=tmp[i];
}
void chuf(int u)
{
memset(tmp,0,sizeof(tmp));int flg=0,cnt=0;
for(int i=5004;i>=1;i--){
tmp[i]=(cnt*10+maxn[i])/a[u].r;
cnt=(cnt*10+maxn[i])%a[u].r;
}
for(int i=5004;i>=1;i--)if(tmp[i]>ans[i]){flg=1;break;}
if(flg)for(int i=5004;i>=1;i--)ans[i]=tmp[i];
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n+1;i++)scanf("%d %d",&a[i].l,&a[i].r);
sort(a+2,a+n+2,cmp);maxn[1]=a[1].l;
for(int i=2;i<=n+1;i++){
chuf(i);
chengf(i);
}
int tail=4004;while(!ans[tail])tail--;//虽然不用改,但没改完太好笑了
for(int i=tail;i>=1;i--)printf("%d",ans[i]);
return 0;
}
三. 开车旅行
1.题意:A先开,去第二小的,B开去第一小的,AB轮流,不能到或者取不到停止,求给定x,A,B开的距离比值最小的sta以及给定sta,x(能开的距离),AB开的距离;
数据范围:对于30%的数据,有1≤N≤20,1≤M≤20;
对于40%的数据,有1≤N≤100,1≤M≤100;
对于50%的数据,有1≤N≤100,1≤M≤1,000;
对于70%的数据,有1≤N≤1,000,1≤M≤10,000;
对于100%的数据,有1≤N≤100,000,1≤M≤10,000,-1,000,000,000≤Hi≤1,000,000,000,0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,数据保证Hi互不相同。
2.注意:
1).距离为海拔高度差,距离相同,海拔低的近;
2)对于第一问,比值相同输出海拔高的;
3.模拟70分,先读取时遍历前面求min1,min2的值和端点(也可以只存端点),然后像链表一样走就行了,这道题正解是集合(或者用双向链表且走完删除也可以)+倍增(其实确定状态(包括谁开)以后就是唯一路径的树了),还没写;
4.代码:
(70)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n,a[100005],min1[100005][2],min2[100005][2],x,maxn,m,sta;
int main()
{
freopen("drive.in","r",stdin);
freopen("drive.out","w",stdout);
memset(min1,127,sizeof(min1));memset(min2,127,sizeof(min2));
scanf("%d",&n);for(int i=1;i<=n;i++){
scanf("%d",&a[i]);for(int j=1;j<i;j++){
int tmp= abs(a[i]-a[j]);
if(tmp<min1[j][0]||(tmp==min1[j][0]&&a[i]<a[j])){
min2[j][1]=min1[j][1];min2[j][0]=min1[j][0];
min1[j][0]=tmp;min1[j][1]=i;}
else if(tmp<min2[j][0]||(tmp==min2[j][0]&&a[i]<a[j])){
min2[j][0]=tmp;min2[j][1]=i;}
}}
scanf("%d",&x);long long maxna=1,maxnb=0;//不能被覆盖;
for(int i=1;i<=n;i++){//边界处理;
long long flg=i,cnt=0,ansa=0,ansb=0;
while(flg<=2e9&&flg!=n){
if(!cnt){
if(ansa+ansb+min2[flg][0]>x)break;
ansa+=(long long)min2[flg][0];
flg=min2[flg][1];
cnt=1;
}
else{
if(ansa+ansb+min1[flg][0]>x)break;
ansb+=(long long)min1[flg][0];
flg=min1[flg][1];
cnt=0;
}
}
if(maxnb==0&&a[maxn]<a[i]){
maxna=ansa;maxnb=ansb;maxn=i;}//特殊情况处理//周全;
else if(ansb&&((double)maxna/maxnb>(double)ansa/ansb||((double)ansb*maxna==(double)maxnb*ansa&&a[maxn]<a[i]))){
maxna=ansa;maxnb=ansb;maxn=i;
}
}
printf("%d",maxn);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&sta,&x);
long long flg=sta,cnt=0,ansa=0,ansb=0;//该改的还是要记得改啊;
while(flg<=2e9&&flg!=n){
if(!cnt){
if((long long)ansa+ansb+min2[flg][0]>(long long)x)break;
ansa+=(long long)min2[flg][0];
flg=min2[flg][1];cnt=1;
}
else{
if((long long)ansa+ansb+min1[flg][0]>(long long)x)break;
ansb+=(long long)min1[flg][0];
flg=min1[flg][1];cnt=0;
}
}
printf("\n%I64d %I64d",ansa,ansb);//输出变量;
}
return 0;
}