/*
俗话说,要有前言:
说起今天的考试,不免有些惭愧
普及组试题,充足的时间,“试友”一半是学弟学妹
而我,虽然可能可以用起步晚来安慰自己(现在是不存在的了),但是这么多人中垫底,rating掉93,是不像话的。
接下来有请题目粗略分析(赶时间)很考试感想出台……
*/
首先,再7点多的时候老刘把题目发下来,断了网,我们拔掉耳机,桌面只留打开的Word和Dev-c++,营造出一个良好的做题环境。
我很听老师的话,准备把每道题都看一遍。
第一题,题目很短,第一反应便是暴力枚举,脑子里留下了念想,但又不敢保证;放着
第二题 添加了一些故事性的内容,整体看来有点像小学数学题,瞧过样例后,自认为样例很良心,看过每一组数据分别是a,b,c三个数,直觉告诉我输出是a+b-c(很直观),仔细看发现真的是这样,兴奋的我生怕待会儿会忘记,就先打破了规矩,先把这题打了,十行不到,样例都过了。接下去看
第三题 题目也很短,也有故事性内容,比如“古天乐”,看样子也是似曾相识的小学数学题,但我不敢轻举妄动,余光瞟到旁边的那位已经开始画这题的图了(有点虚)。继续
第四题 像这种格子跳啊跳的第一反应就是搜索,但我不擅长这茬,就算是模板,也不敢轻举妄动,留着
第五题因为记忆中习惯于四道题,第一次没有往下看,题目做了一半之后才意外发现,第一反应觉得是数学题,全排列之类的,有点想法又不会操作。嗯就是这样。
(以下题目不知是否改编,纯属OJ原题目)
T1 Classroom Watch
题目大意:
给出一个正整数 n,现在问存在多少个 x,使得 x在十进制下的每一位之和加上 x 等于 n。
老刘说,因为考虑到大众感受,这是原本没有,新加的题(疯狂暗示很水,为下文我的小题大做做铺垫,形成强烈对比,突出我很二的个性……)
数据范围貌似挺大,刚刚的暴力想法瞬间蒸发。然后我开始立马低头推数学方法,用了一整张草稿纸。推导过程中我发现不管是n还是x,各个位数之和是<81的,但沉醉于推到兴头的我没有意识到完全可以从n-100~n的范围内枚举x,只要符合条件就可以,因此我走向了不归之路。推出来后,自认为非常不错,还在Dev里备注了思路,如图(对于我这种数学渣子来说,心中很有成就感)
花了大把时间的苦工,结果只有30分,我表示不再相信自信心……最后我就是老师所说的“简单题目复杂化”之人……
然而正解就不必多说了:
#include<bits/stdc++.h>
using namespace std;
int n;
int ans[5555];
int main()
{
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
scanf("%d",&n);
int cnt=0;
for(int i=max(1,n-1000);i<=n;i++)
{
int j=i,sum=0;
while(j>0)
{
sum+=j%10;
j/=10;
}
if(sum+i==n) ans[++cnt]=i;
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
// cout<<ans[i]<<endl;
printf("%d\n",ans[i]);
return 0;
}
T2 组合技能combo
题目大意:
给定A、B、C三个数,使得A-a=B-b=C-a-b。求出A-a(即B-b,C-a-b)利润,数据保证为正数。
老刘说他答应给我们考一次A+B,然后这题就出现了(良心!/竖起了大拇指)
这个相信那些数学大佬都不用动脑子,从样例里也能恰好看出一定规律,这说明考试中的直觉也是比较重要的。那么不必多说,直接将做法(因为能够自己一遍A,说话也比较有底气)
思路:
设A-a=B-b=C-a-b=x
则需解x
A=x+a,B=x+b,C=x+a+b
x=A+B-C=x+x+a+b-(x+a+b)=x
Perfect
代码(超简短):
#include<bits/stdc++.h>
using namespace std;
int main()
{
freopen("combo.in","r",stdin);
freopen("combo.out","w",stdout);
int t;
cin>>t;
for(int i=1;i<=t;i++)
{
int a,b,c;
cin>>a>>b>>c;
cout<<a+b-c<<endl;
}
return 0;
}
T3 表面积
题目大意:
积木图可以抽象为一个n*m的网格图,其中第(i,j)的位置有A[i][j]个积木。求表面积。
看了如此有内涵的图顿时有点懵,但是又是求表面积,联想到小学数学题也就安心了。表面积就是内看到的面积,即没有并在一起的面积,没被遮住就没有被并在一起,比周围的某一个高的地方就没有被遮住。因为除了读入数据,其他坐标的高度默认为0,而且题目非常良心地说“1<=a[i][j]<=100”。除了俯视,即上表面,和下表面一定都是n*m外,就只要求侧面积即可。那么枚举每一个坐标高度,累加比四周坐标高出的部分即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long ans;
int a[200][200];
int main()
{
freopen("surface.in","r",stdin);
freopen("surface.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int fr=max(0,a[i][j]-a[i-1][j]);
int beh=max(0,a[i][j]-a[i+1][j]);
int l=max(0,a[i][j]-a[i][j-1]);
int r=max(0,a[i][j]-a[i][j+1]);
ans=ans+(long long)(fr+beh+l+r);
}
printf("%lld\n",ans+2*n*m);
return 0;
}
和意外的是我的这一题爆0了,原因是文件名打错,少写了一个"r"
T4 红皇后的旅行
题目大意:
给定一个n*n的棋盘,行和列标号为0,1,2,….,n-1。在棋盘的(i_start,j_start)位置上有一位红皇后,每次红皇后可以往六个方向走,如图所示:
现在红皇后想去(i_end,j_end)点,求最短距离,并且输出一条路径。
显然最短路径有无穷条,请按照以下顺序来搜索:UL, UR, R, LR, LL, L。
如果无解,输出Impossible
Sample Input
7
6 6 0 1
Sample Output
4
UL UL UL L
Sample Explain
这近乎于SPFA模板题,只要加一些优化即可,而我并没有做出来,输出“Impossible”会有36分/xk
很奇怪,对于搜索,我不管复习几遍都好像没有很大的把我能写出来,我认为,搜索的那些模板和最短路的好像好像,虽然我之前相以有趣的方式加强对最短路的印象与理解写了一篇“近来算法想象篇”,结果并没什么卵用。
代码应该无需过多解释了:
#include<bits/stdc++.h>
using namespace std;
int n;
int sx,sy,ex,ey;
int vis[500][500],dis[500][500];
string towards[10]={"UL","UR","R","LR","LL","L"};
int dx[6]={-2,-2,0,2,2,0};
int dy[6]={-1,1,2,1,-1,-2};
struct loc
{
int x,y;
}from[500][500];//from[i][j]记录从哪个坐标走到(i,j)
queue<loc> q;
void go()//SPFA
{
for(int i=0;i<=n+100;i++)
for(int j=0;j<=n+100;j++)
dis[i][j]=500000000;
dis[sx][sy]=0;
vis[sx][sy]=0;
loc a;
a.x=sx,a.y=sy;
q.push(a);
while(!q.empty())
{
a=q.front();
q.pop();
vis[a.x][a.y]=0;
for(int i=0;i<6;i++)
{
loc aa;
aa.x=a.x+dx[i];
aa.y=a.y+dy[i];
if(aa.x<0||aa.x>=n||aa.y<0||aa.y>=n)
continue;
if(dis[a.x][a.y]+1<dis[aa.x][aa.y])
{
dis[aa.x][aa.y]=dis[a.x][a.y]+1;
from[aa.x][aa.y].x=a.x;
from[aa.x][aa.y].y=a.y;
if(!vis[aa.x][aa.y])
{
vis[aa.x][aa.y]=1;
q.push(aa);
}
}
}
}
}
void get_way(int frx,int fry,int tox,int toy)
{
if(frx==0&&fry==0)return;
get_way(from[frx][fry].x,from[frx][fry].y,frx,fry);
for(int i=0;i<6;i++)
if(frx+dx[i]==tox&&fry+dy[i]==toy)
{
cout<<towards[i]<<' ';
break;
}
return;
}
int main()
{
freopen("redqueen.in","r",stdin);
freopen("redqueen.out","w",stdout);
scanf("%d",&n);
scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
go();
if(dis[ex][ey]==500000000)
printf("Impossible\n");
else
{
printf("%d\n",dis[ex][ey]);
get_way(ex,ey,0,0);
}
return 0;
}
T5 构造序列
题目大意:
有一个长度为n的序列A,其中A[1]=1,A[n]=x,A[2…n-1]可以是1至k间任意一个正整数。求有多少个不同的序列,使得相邻两个数不同。
答案对10^9+7取模。
上文说了,这道题实在之后看到的,而且觉得它像排列组合,但实际上并不是,因此我也没写出来。
很显然,题目规定第一个数固定为1,那么不考虑后面对当前的影响,共有K个数,相邻的数不能相同,因此每次*(k-1);但是其中第n-1个位置会包括与n位置上(n位置上固定为数x)的数相同的情况,这不符合题意所以在上面的操作之后要减去以n-1这个位置为x的时候的方案数
详情看代码注释:
#include<bits/stdc++.h>
using namespace std;
int n,k,x;
long long o_f[255555];//only_first
//题中固定第一个为1,不考虑最后一个的影响的到第i个数的方案数
long long now_x[255555];
//第一个数固定为1,如果当前数是x(即固定的一个数)时,到当前数的方案数
long long const mod=1000000007;
int main()
{
freopen("construct.in","r",stdin);
freopen("construct.out","w",stdout);
cin>>n>>k>>x;
o_f[2]=k-1;
for(int i=3;i<n;i++)
o_f[i]=o_f[i-1]*(k-1)%mod;//共有K个数,相邻的数不能相同,因此*(k-1)
if(x==1)
now_x[2]=0;//表示第而个数的位置是固定的x,且x=1,但题目固定第一个数为1,又不能相邻的相同,因此没有方案
else
now_x[2]=1;
for(int i=3;i<=n;i++)
now_x[i]=(o_f[i-1]-now_x[i-1]+mod)%mod; //以当前数为x时,前一个数不考虑x影响的方案数减去以前一个数为x的方案数(因为前一个数为x,当前数又为x不符合要求)
cout<<now_x[n]<<endl;
return 0;
}
感谢老师和大佬有请讲解,帮同学们过了这一题!