因能力有限,题解部分参照前辈想法,并加以博主思考。
如有不足,欢迎指正~!
后半部分题解较为潦草简单,详细思路后补~
其中试题G样例通过,实际思路上有些错误;
试题J规模、时间过不了;
样例F、I尚未解决~
试题A:美丽的2
题目:
【问题描述】
小蓝特别喜欢2,今年是公元2020年,他特别高兴。
他很好奇,在公元1年到公元2020年(包含)中,有多少个年份的数位中包含数字2?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:
563
题解:
本题是道简单的签到题,题目明确提出:
在 [2,2020] 中,有多少个数包含数字2
遍历每个数中的每位数,倘若是2,则计数
加注释代码如下……
#include<iostream>
#include<cstdio>
using namespace std;
int cnt=0; // 计数
bool judge(int); // 判断
int main(){
int i;
for(i=1;i<=2020;++i){
if(judge(i)) ++cnt;
} // 遍历
printf("%d",cnt);
return 0;
}
bool judge(int n){
while(n){
if(n%10==2) return true;
n/=10;
} // 遍历并判断各数位上的数是否等于2
return false;
}
试题B:扩散
题目:
【问题描述】
小蓝在一张无限大的特殊画布上作画。
这张画布可以看成一个方格图,每个格子可以用一个二维的整数坐标表示。
小蓝在画布上首先点了一下几个点:(0, 0), (2020, 11), (11, 14), (2000, 2000)。只有这几个格子上有黑色,其它位置都是白色的。
每过一分钟,黑色就会扩散一点。具体的,如果一个格子里面是黑色,它就会扩散到上、下、左、右四个相邻的格子中,使得这四个格子也变成黑色(如果原来就是黑色,则还是黑色)。
请问,经过 2020 分钟后,画布上有多少个格子是黑色的。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:
20312088
题解:
本题有两种解法
1、广搜BFS
2、数学分析(姑且叫这个吧)
Method 1(多源BFS):
题目要求是有四个点,分别向四周扩散2020次,计算被污染的格子数量
很明显能够想到使用BFS,这里有三个注意点:
1、画布无限大,0不做边界,因此我们需要在给出点的基础上做偏移
2、本题给出四个点,与其说多源BFS,实际就是将四点都压入广搜队列中依次遍历
3、除却坐标 ( x , y ) (x,y) (x,y) 外,还有时间 t t t ( t < = 2020 t<=2020 t<=2020 )。因此博主在解决本题时,额外建立结构 pt (x,y,t)
运行时间较长,要好几秒
加注释代码如下……
// Method 1
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int MAXN=1e4;
const int T=4000; // 偏移量t
struct pt{
int x,y,t; // 坐标(x,y)以及遍历的时间点t
pt(int tx,int ty,int tt):x(tx),y(ty),t(tt){ } // 含参构造函数
}; // 画布点的结构
int turn[4][2]={-1,0,1,0,0,1,0,-1};
int ans=0;
bool table[MAXN+10][MAXN+10]; // 二维画布
int p[4][2]={{0,0},{2020,11},{11,14},{2000,2000}}; // 初始四点
int main(){
int i;
int x,y,t;
int tx,ty;
// pt结构的队列,用于bfs
queue<pt> q;
// 将4个点置入队列,并在画布标记
for(i=0;i<4;++i){
x=p[i][0]; y=p[i][1];
q.push(pt(x+T,y+T,0)); // 要加偏移量
table[x+T][y+T]=1;
}
// BFS
while(!q.empty()){
++ans;
// 将结构队列首元素的坐标和时间分别赋值给x,y,t
x=q.front().x;
y=q.front().y;
t=q.front().t;
q.pop();
// 边界状态(时间点t>=2020),即经过2020分钟后
if(t>=2020) continue;
for(i=0;i<4;++i){
tx=x+turn[i][0];
ty=y+turn[i][1];
if(!table[tx][ty]){
table[tx][ty]=1;
q.push(pt(tx,ty,t+1));
}
}// 遍历上下左右,将未被标记的格子入队
}
printf("%d",ans);
return 0;
}
Method 2(数学分析):
该方法运行时间比多源BFS更少些,但是不知道为什么测试数据的时候会显示运行错误
分析题目发现,扩散结果如下图所示一般:
据此,博主想到了刚学高级语言程序设计时的一道有关输出的简单样题:输出由 * 组成的图形:
*
* *
* * *
* *
*明显发现,本题扩散结果类似上述形状
从四个点单独考虑、分别遍历,最后再计算被遍历次数大于0的格子个数即可
加注释代码如下……
// Method 2
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=1e4;
const int T=4000; // 转移量
int table[MAXN+10][MAXN+10];
// 各点初始坐标
int p[4][2]={ {0, 0}, {2020, 11}, {11, 14}, {2000, 2000} };
// 单格(x,y)扩散时,画布的变化
void change(int,int);
// 获取画布黑色格子个数(table[i][j]>0)
int getAns();
int main(){
int i,x,y;
for(i=0;i<4;++i){
x=p[i][0]+T; y=p[i][1]+T;
change(x,y);
}// 从四个点分别扩散
printf("%d",getAns());
return 0;
}
void change(int x,int y){
int i,j;
for(i=0;i<=2020;++i){
for(j=0;j<=2020-i;++j){
++table[x+i][y-j];
++table[x+i][y+j];
++table[x-i][y-j];
++table[x-i][y+j];
}
}// 寻找每行的被标记始末位置,进行变化
}
int getAns(){
int i,j;
int ans=0;
for(i=0;i<=MAXN;++i){
for(j=0;j<=MAXN;++j){
if(table[i][j])++ans;
}
}//遍历画布,获取黑色格子数量
return ans;
}
试题C:阶乘约数
题目
【问题描述】
定义阶乘 n! = 1 × 2 × 3 × · · · × n。
请问 100! (100 的阶乘)有多少个正约数。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:
39001250856960000
题解:
本题求解数 n! 的正约数个数
解决本题时,需使用素数筛(如埃式筛法等)以及约数个数定理( f ( n ) = ∏ i = 1 k ( a i + 1 ) f(n) =\displaystyle\prod_{i=1}^k(a_{i}+1) f(n)=i=1∏k(ai+1), a i a_{i} ai 是第i个质因数的个数)
本题是求阶乘的约数个数,阶乘 n ! = 1 × 2 × 3 × . . . × n n!=1×2×3×...×n n!=1×2×3×...×n
可以利用这个定义,通过循环分别获取 [ 1 , n ] [1,n] [1,n] 中的每个数所包含的质因数及其个数,并将每个质因数个数累加起来,最后根据约数个数定理求积即可
加注释代码如下……
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
typedef map<int,int>::iterator iter;
map<int,int> prime; // 素数个数
// 埃式筛
void findPrime(int);
// 获取正约数个数
LL getAns();
int main(){
int i;
for(i=2;i<=100;++i){
findPrime(i);
} // 遍历阶乘所有乘数
printf("%lld",getAns()); // 输出
return 0;
}
void findPrime(int n){
int cnt; // 当前素数个数
// 素数筛↓
for(int i=2;i<=sqrt(n)&&n>1;++i){
cnt=0;
if(n%i==0){
while(n%i==0){
n/=i;
++cnt;
}
}
prime[i]=prime[i]+cnt;
}
if(n>1){
prime[n]=prime[n]+1;
}
}
LL getAns(){
LL ans=1;
for(iter it=prime.begin();it!=prime.end();++it){
ans*= (it->second+1);
}// 约数公式 ans = sum( numPrime[i] + 1 )
return ans;
}
试题D:本质上升序列
题目:
【问题描述】
小蓝特别喜欢单调递增的事物。
在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。
例如,在字符串 lanqiao 中,如果取出字符 n 和 q,则 nq 组成一个单调递增子序列。类似的单调递增子序列还有 lnq、i、ano 等等。
小蓝发现,有些子序列虽然位置不同,但是字符序列是一样的,例如取第二个字符和最后一个字符可以取到 ao,取最后两个字符也可以取到 ao。小蓝认为他们并没有本质不同。
对于一个字符串,小蓝想知道,本质不同的递增子序列有多少个?
例如,对于字符串 lanqiao,本质不同的递增子序列有 21 个。它们分别是 l、a、n、q、i、o、ln、an、lq、aq、nq、ai、lo、ao、no、io、lnq、anq、lno、ano、aio。
请问对于以下字符串(共 200 个小写英文字母,分四行显示):(如果你把以下文字复制到文本文件中,请务必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 inc.txt,内容与下面的文本相同)
tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
本质不同的递增子序列有多少个?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:
3616159
题解:
1、dp去重(代码如下)
2、前缀和(新思路,尚未实现)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char str[205];
int ans=0;
int dp[205];
int main(){
scanf("%s",str);
int len=strlen(str);
int i,j;
fill(dp,dp+200,1);
for(i=0;i<len;++i){
for(j=0;j<i;++j){
if(str[j]<str[i]) dp[i]+=dp[j];
if(str[j]==str[i])dp[i]-=dp[j];
}
ans+=dp[i];
}
printf("%d",ans);
return 0;
}
试题E:玩具蛇
题目:
【问题描述】
小蓝有一条玩具蛇,一共有 16 节,上面标着数字 1 至 16。每一节都是一个正方形的形状。相邻的两节可以成直线或者成 90 度角。
小蓝还有一个 4 × 4 的方格盒子,用于存放玩具蛇,盒子的方格上依次标着字母 A 到 P 共 16 个字母。
小蓝可以折叠自己的玩具蛇放到盒子里面。他发现,有很多种方案可以将玩具蛇放进去。
下图给出了两种方案:
请帮小蓝计算一下,总共有多少种不同的方案。如果两个方案中,存在玩具蛇的某一节放在了盒子的不同格子里,则认为是不同的方案。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:
552
题解:
DFS(常规)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int turn[4][2]={-1,0,1,0,0,1,0,-1}; // 用于点位移
bool table[5][5]; // 标记数组
int ans=0;
void dfs(int,int,int); // dfs(x,y,index)
int main(){
int i,j;
for(i=1;i<=4;++i){
for(j=1;j<=4;++j){
memset(table,0,sizeof(table));
table[i][j]=1;
dfs(i,j,1);
}
} // 改变起点位置,多源dfs
printf("%d",ans);
return 0;
}
void dfs(int x,int y,int index){
if(index>=16){
++ans; return;
} // dfs边界
int i;
int tx,ty;
for(i=0;i<4;++i){
tx=x+turn[i][0];
ty=y+turn[i][1];
// 不符合条件 continue
if(tx<1||ty<1||tx>4||ty>4||table[tx][ty]) continue;
table[x][y]=1; // 格子被使用
dfs(tx,ty,index+1); // 继续深搜
table[x][y]=0; // 恢复标志状态
} // 遍历四周,符合条件则继续深搜
}
试题F:皮亚诺曲线距离
题目:
【问题描述】
皮亚诺曲线是一条平面内的曲线。
下图给出了皮亚诺曲线的 1 阶情形,它是从左下角出发,经过一个 3 × 3 的方格中的每一个格子,最终到达右上角的一条曲线。
下图给出了皮亚诺曲线的 2 阶情形,它是经过一个 3 2 × 3 2 3^2 × 3^2 32×32 的方格中的每一个格子的一条曲线。它是将 1 阶曲线的每个方格由 1 阶曲线替换而成。
下图给出了皮亚诺曲线的 3 阶情形,它是经过一个 3 3 × 3 3 3^3 × 3^3 33×33的方格中的每一个格子的一条曲线。它是将 2 阶曲线的每个方格由 1 阶曲线替换而成。
皮亚诺曲线总是从左下角开始出发,最终到达右上角。
我们将这些格子放到坐标系中,对于 k k k 阶皮亚诺曲线,左下角的坐标是(0, 0),右上角坐标是 ( 3 k 3^k 3k − 1, 3 k 3^k 3k − 1),右下角坐标是 ( 3 k 3^k 3k − 1, 0),左上角坐标是(0, 3 k 3^k 3k − 1)。
给定 k k k 阶皮亚诺曲线上的两个点的坐标,请问这两个点之间,如果沿着皮亚诺曲线走,距离是到少?
【输入格式】
输入的第一行包含一个正整数 k k k,皮亚诺曲线的阶数。
第二行包含两个整数 x 1 x_1 x1, y 1 y_1 y1,表示第一个点的坐标。
第三行包含两个整数 x 2 x_2 x2, y 2 y_2 y2,表示第二个点的坐标。
【输出格式】
输出一个整数,表示给定的两个点之间的距离。
【样例输入】
1
0 0
2 2
【样例输出】
8
【样例输入】
2
0 2
0 3
【样例输出】
13
【评测用例规模与约定】
对于 30% 的评测用例, 0 ≤ k ≤ 10 0 ≤ k ≤ 10 0≤k≤10。
对于 50% 的评测用例, 0 ≤ k ≤ 20 0 ≤ k ≤ 20 0≤k≤20。
对于所有评测用例, 0 ≤ k ≤ 100 0 ≤ k ≤ 100 0≤k≤100, 0 ≤ x 1 , y 1 , x 2 , y 2 < 3 k 0 ≤ x_1, y_1, x_2, y_2 < 3k 0≤x1,y1,x2,y2<3k, x 1 , y 1 , x 2 , y 2 ≤ 1 0 18 x_1, y_1, x_2, y_2 ≤ 10^{18} x1,y1,x2,y2≤1018。数据保证答案不超过 1 0 18 10^{18} 1018。
试题G:游园安排
题目:
【问题描述】
L 星球游乐园非常有趣,吸引着各个星球的游客前来游玩。小蓝是 L 星球游乐园的管理员。
为了更好的管理游乐园,游乐园要求所有的游客提前预约,小蓝能看到系统上所有预约游客的名字。个游客的名字由一个大写英文字母开始,后面跟0 个或多个小写英文字母。游客可能重名。
小蓝特别喜欢递增的事物。今天,他决定在所有预约的游客中,选择一部分游客在上午游玩,其他的游客都在下午游玩,在上午游玩的游客要求按照预约的顺序排列后,名字是单调递增的,即排在前面的名字严格小于排在后面的名字。
一个名字 A 小于另一个名字 B 是指:存在一个整数 i,使得 A 的前 i 个字母与 B 的前 i 个字母相同,且 A 的第 i+ 1 个字母小于 B 的第 i+ 1 个字母。(如果 A 不存在第 i + 1 个字母且 B 存在第 i + 1 个字母,也视为 A 的第 i + 1 个字母小于 B 的第 i + 1 个字母)
作为小蓝的助手,你要按照小蓝的想法安排游客,同时你又希望上午有尽量多的游客游玩,请告诉小蓝让哪些游客上午游玩。如果方案有多种,请输出上午游玩的第一个游客名字最小的方案。如果此时还有多种方案,请输出第一个游客名字最小的前提下第二个游客名字最小的方案。如果仍然有多种,依此类推选择第三个、第四个……游客名字最小的方案。
【输入格式】
输入包含一个字符串,按预约的顺序给出所有游客的名字,相邻的游客名字之间没有字符分隔。
【输出格式】
按预约顺序输出上午游玩的游客名单,中间不加任何分隔字符。
【样例输入】
WoAiLanQiaoBei
【样例输出】
AiLanQiao
【评测用例规模与约定】
对于 20% 的评测数据,输入的总长度不超过 20 个字母。
对于 50% 的评测数据,输入的总长度不超过 300 个字母。
对于 70% 的评测数据,输入的总长度不超过 10000 个字母。
对于所有评测数据,每个名字的长度不超过 10 个字母,输入的总长度不超过1000000 个字母。
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
struct node{
string str; // 排好队的字符串
string last; // 最后一个游客名字
int lenth; // 队伍长度
node(string str,string last,int lenth){
this->str=str;
this->last=last;
this->lenth=lenth;
}//含参构造
};
typedef vector<node>::iterator iter;
bool cmp(node a,node b){
if(a.lenth>b.lenth){
return true;
}
return a.str<b.str;
}
vector<node> vec;
int main(){
char ch;
string s;
do{
ch=getchar(); // 开始输入
if(ch<'a'){
if(s!=""){
for(iter it=vec.begin();it!=vec.end();++it){
if(it->last<s){
it->str+=s;
it->last=s;
++it->lenth;
}
} // 遍历
vec.push_back(node(s,s,1));
}
s=ch;
} // 输入新名字
else{
s+=ch;
}
}while(ch!='\n');
sort(vec.begin(),vec.end(),cmp);
cout<<(vec.begin())->str;
return 0;
}
试题H:答疑
题目:
【问题描述】
有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。
老师可以安排答疑的顺序,同学们要依次进入老师办公室答疑。
一位同学答疑的过程如下:
- 首先进入办公室,编号为 i 的同学需要 s i s_i si 毫秒的时间。
- 然后同学问问题老师解答,编号为 i 的同学需要 a i a_i ai 毫秒的时间。
- 答疑完成后,同学很高兴,会在课程群里面发一条消息,需要的时间可以忽略。
- 最后同学收拾东西离开办公室,需要 e i e_i ei 毫秒的时间。一般需要 10 秒、20 秒或 30 秒,即 e i e_i ei 取值为 10000,20000 或 30000。
一位同学离开办公室后,紧接着下一位同学就可以进入办公室了。
答疑从 0 时刻开始。老师想合理的安排答疑的顺序,使得同学们在课程群里面发消息的时刻之和最小。
【输入格式】
输入第一行包含一个整数 n,表示同学的数量。
接下来 n 行,描述每位同学的时间。其中第 i 行包含三个整数 s i s_i si, a i a_i ai, e i e_i ei,意义如上所述。
【输出格式】
输出一个整数,表示同学们在课程群里面发消息的时刻之和最小是多少。
【样例输入】
3
10000 10000 10000
20000 50000 20000
30000 20000 30000
【样例输出】
280000
【样例说明】
按照 1, 3, 2 的顺序答疑,发消息的时间分别是 20000, 80000, 180000。
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ n ≤ 20。
对于 60% 的评测用例,1 ≤ n ≤ 200。
对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ s i s_i si ≤ 60000,1 ≤ a i a_i ai ≤ 1000000, e i e_i ei ∈ {10000, 20000, 30000},即 e i e_i ei 一定是 10000、20000、30000 之一。
题解:
开始想的复杂了,感觉要用dp+前缀和,后来看了下其他博主的思路,发现该题正解是多条件的整体排序(也就是用个结构,写个cmp函数或重载小于号,再用sort快排)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=1000;
struct student{
int s,e; // s(start + act), e(end)
int num; // num(s+a+e)
student(){
s=0,e=0,num=0;
} // 默认构造函数
student(int s,int a,int e){
this->s=s+a;
this->e=e;
this->num=s+a+e;
} // 含参构造函数
bool operator<(student tmp){
if(this->num==tmp.num) return this->s<tmp.s;
return this->num<tmp.num;
} // 重载 <
};
int n; // 同学的数量
student vec[MAXN+10];
void inif(); // 初始化
LL ans();
int main(){
inif();
printf("%lld",ans());
return 0;
}
// 初始化
void inif(){
int i,s,a,e;
scanf("%d",&n);
for(i=0;i<n;++i){
scanf("%d%d%d",&s,&a,&e);
vec[i]=student(s,a,e);
}
}
LL ans(){
int i;
LL t=0,tmp=0;
sort(vec,vec+n); // 排序
for(i=0;i<n;++i){
tmp+=vec[i].s;
t+=tmp;
tmp+=vec[i].e;
} //计算 t
return t;
}
试题I:出租车
问题:
【问题描述】
小蓝在 L 市开出租车。
L 市的规划很规整,所有的路都是正东西向或者正南北向的,道路都可以看成直线段。东西向的道路互相平行,南北向的道路互相平行,任何一条东西向道路垂直于任何一条南北向道路。
从北到南一共有 n 条东西向道路,依次标号为 H1, H2, · · · , Hn。从西到东一共有 m 条南北向的道路,依次标号为 S 1, S 2, · · · , S m。
每条道路都有足够长,每一条东西向道路和每一条南北向道路都相交,Hi 与 S j 的交叉路口记为 (i, j)。
从 H1 和 S 1 的交叉路口 (1, 1) 开始,向南遇到的路口与 (1, 1) 的距离分别是 h1, h2, · · · , hn−1,向东遇到路口与 (1, 1) 的距离分别是 w1, w2, · · · , wm−1。
道路的每个路口都有一个红绿灯。
时刻 0 的时候,南北向绿灯亮,东西向红灯亮,南北向的绿灯会持续一段时间(每个路口不同),然后南北向变成红灯,东西向变成绿灯,持续一段时间后,再变成南北向绿灯,东西向红灯。
已知路口 (i, j) 的南北向绿灯每次持续的时间为 gi j,东西向的绿灯每次持续的时间为 ri j,红绿灯的变换时间忽略。
当一辆车走到路口时,如果是绿灯,可以直行、左转或右转。如果是红灯,可以右转,不能直行或左转。如果到路口的时候刚好由红灯变为绿灯,则视为看到绿灯,如果刚好由绿灯变为红灯,则视为看到红灯。
每段道路都是双向道路,道路中间有隔离栏杆,在道路中间不能掉头,只能在红绿灯路口掉头。掉头时不管是红灯还是绿灯都可以直接掉头。掉头的时间可以忽略。
小蓝时刻 0 从家出发。今天,他接到了 q 个预约的订单,他打算按照订单的顺序依次完成这些订单,就回家休息。中途小蓝不准备再拉其他乘客。
小蓝的家在两个路口的中点,小蓝喜欢用 x1, y1, x2, y2 来表示自己家的位置,即路口 (x1, y1) 到路口 (x2, y2) 之间的道路中点的右侧,保证两个路口相邻(中间没有其他路口)。请注意当两个路口交换位置时,表达的是路的不同两边,路中间有栏杆,因此这两个位置实际要走比较远才能到达。
小蓝的订单也是从某两个路口间的中点出发,到某两个路口间的中点结束。小蓝必须按照给定的顺序处理订单,而且一个时刻只能处理一个订单,不能图省时间而同时接两位乘客,也不能插队完成后面的订单。
小蓝只对 L 市比较熟,因此他只会在给定的 n 条东西向道路和 m 条南北向道路上行驶,而且不会驶出 H1, Hn, S 1, S m 这几条道路所确定的矩形区域(可以到边界)。
小蓝行车速度一直为 1,乘客上下车的时间忽略不计。
请问,小蓝最早什么时候能完成所有订单回到家。
【输入格式】
输入第一行包含两个整数 n, m,表示东西向道路的数量和南北向道路的数量。
第二行包含 n − 1 个整数 h1, h2, · · · , hn−1。
第三行包含 m − 1 个整数 w1, w2, · · · , wm−1。
接下来 n 行,每行 m 个整数,描述每个路口南北向绿灯的时间,其中的第i 行第 j 列表示 gi j。
接下来 n 行,每行 m 个整数,描述每个路口东西向绿灯的时间,其中的第i 行第 j 列表示 ri j。
接下来一行包含四个整数 x1, y1, x2, y2,表示小蓝家的位置在路口 (x1, y1)到路口 (x2, y2) 之间的道路中点的右侧。
接下来一行包含一个整数 q,表示订单数量。
接下来 q 行,每行描述一个订单,其中第 i 行包含八个整数 xi1, yi1, xi2, yi2, xi3, yi3, i4, yi4,表示第 i 个订单的起点为路口 (xi1, yi1) 到路口 (xi2, yi2) 之间的道路中点的右侧,第 i 个订单的终点为路口 (xi3, yi3) 到路口 (xi4, yi4) 之间的道路中点的右侧。
【输出格式】
输出一个实数,表示小蓝完成所有订单最后回到家的最早时刻。四舍五入保留一位小数。
【样例输入】
2 3
200
100 400
10 20 10
20 40 30
20 20 20
20 20 20
2 1 1 1
1
2 2 1 2 1 2 1 3
【样例输出】
1620.0
【样例说明】
小蓝有一个订单,他的行车路线如下图所示。其中 H 表示他家的位置,S表示订单的起点,T 表示订单的终点。小明在最后回家时要在直行的红绿灯路口等绿灯,等待时间为 20。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n, m ≤ 5,1 ≤ q ≤ 10。
对于 50% 的评测用例,1 ≤ n, m ≤ 30,1 ≤ q ≤ 30。
对于所有评测用例,1 ≤ n, m ≤ 100,1 ≤ q ≤ 30,1 ≤ h1 < h2 < · · · < hn−1 ≤100000,1 ≤ w1 < w2 < · · · < wm−1 ≤ 100000,1 ≤ gi j ≤ 1000,1 ≤ ri j ≤ 1000,给定的路口一定合法。
试题J:质数行者
问题:
【问题描述】
小蓝在玩一个叫质数行者的游戏。
游戏在一个 n × m × w 的立体方格图上进行,从北到南依次标号为第 1 行到第 n 行,从西到东依次标号为第 1 列到第 m 列,从下到上依次标号为第 1 层到第 w 层。
小蓝要控制自己的角色从第 1 行第 1 列第 1 层移动到第 n 行第 m 列第 w层。每一步,他可以向东走质数格、向南走质数格或者向上走质数格。每走到一个位置,小蓝的角色要稍作停留。
在游戏中有两个陷阱,分别为第 r 1 r_1 r1 行第 c 1 c_1 c1 列第 h 1 h_1 h1 层和第 r 2 r_2 r2 行第 c 2 c_2 c2 列第 h 2 h_2 h2 层。这两个陷阱的位置可以跨过,但不能停留。也就是说,小蓝不能控制角色某一步正好走到陷阱上,但是某一步中间跨过了陷阱是允许的。
小蓝最近比较清闲,因此他想用不同的走法来完成这个游戏。所谓两个走法不同,是指小蓝稍作停留的位置集合不同。
请帮小蓝计算一下,他总共有多少种不同的走法。
提示:请注意内存限制,如果你的程序运行时超过内存限制将不得分。
【输入格式】
输入第一行包含两个整数 n, m, w,表示方格图的大小。
第二行包含 6 个整数, r 1 r_1 r1, c 1 c_1 c1, h 1 h_1 h1, r 2 r_2 r2, c 2 c_2 c2, h 2 h_2 h2,表示陷阱的位置。
【输出格式】
输出一行,包含一个整数,表示走法的数量。答案可能非常大,请输出答案除以 1000000007 的余数。
【样例输入】
5 6 1
3 4 1 1 2 1
【样例输出】
11
【样例说明】
用 (r, c, h) 表示第 r 行第 c 列第 h 层,可能的走法有以下几种:
- (1, 1, 1) − (1, 3, 1) − (1, 6, 1) − (3, 6, 1) − (5, 6, 1)。
- (1, 1, 1) − (1, 3, 1) − (3, 3, 1) − (3, 6, 1) − (5, 6, 1)。
- (1, 1, 1) − (1, 3, 1) − (3, 3, 1) − (5, 3, 1) − (5, 6, 1)。
- (1, 1, 1) − (3, 1, 1) − (3, 3, 1) − (3, 6, 1) − (5, 6, 1)。
- (1, 1, 1) − (3, 1, 1) − (3, 3, 1) − (5, 3, 1) − (5, 6, 1)。
- (1, 1, 1) − (3, 1, 1) − (5, 1, 1) − (5, 3, 1) − (5, 6, 1)。
- (1, 1, 1) − (3, 1, 1) − (5, 1, 1) − (5, 4, 1) − (5, 6, 1)。
- (1, 1, 1) − (1, 4, 1) − (1, 6, 1) − (3, 6, 1) − (5, 6, 1)。
- (1, 1, 1) − (1, 6, 1) − (3, 6, 1) − (5, 6, 1)。
- (1, 1, 1) − (3, 1, 1) − (3, 6, 1) − (5, 6, 1)。
- (1, 1, 1) − (3, 1, 1) − (5, 1, 1) − (5, 6, 1)。
【评测用例规模与约定】
对于 30% 的评测用例 1 ≤ n, m,w ≤ 50。
对于 60% 的评测用例 1 ≤ n, m,w ≤ 300。
对于所有评测用例,1 ≤ n, m, w ≤ 1000,1 ≤ r 1 r_1 r1, r 2 r_2 r2 ≤ n, 1 ≤ c 1 c_1 c1, c 2 c_2 c2 ≤ m, 1 ≤ h 1 h_1 h1, h 1 h_1 h1 ≤ w,陷阱不在起点或终点,两个陷阱不同。
题解:
水平有些,只能按照记忆化搜索写,已经想不到还怎么怎么减规模了,请大佬们多指点指点本蒟蒻。
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=300+10; // 规模限制
const int MOD=1e9+7; // 取模值
int prime[MAXN],np=0; // 存储0~1000的素数,np记录范围内素数个数
void findPrime(); // 素数筛
int n,m,w; // 1 ≤ n, m, w ≤ 1000
int r[2],c[2],h[2]; // 陷阱
int dp[MAXN][MAXN][MAXN]; // 记忆化搜索三维数组
void inif(); // 初始化
bool judge(int,int,int); // 判断有无遇到陷阱
void getAns(); // 解题
int main(){
inif();
getAns();
printf("%d",dp[n][m][w]);
return 0;
}
// 素数筛
void findPrime(){
int i,j;
bool isPrime[MAXN];
for(i=2;i<=MAXN;++i){
if(isPrime[i]) continue;
prime[np++]=i;
for(j=i;j<=MAXN;j+=i){
isPrime[j]=1;
}
}
}
// 初始化
void inif(){
int i;
scanf("%d%d%d",&n,&m,&w);
for(i=0;i<2;++i){
scanf("%d%d%d",r+i,c+i,h+i);
}
dp[1][1][1]=1;
findPrime();
}
// 判断有无遇到陷阱
bool judge(int i,int j,int k){
for(int l=0;l<2;++l){
if(i==r[l]&&j==c[l]&&k==h[l]) return true;
}
return false;
}
// 解题
void getAns(){
int i,j,k,l;
int tmp,cnt;
for(i=1;i<=n;++i){
for(j=1;j<=m;++j){
for(k=1;k<=w;++k){
if(dp[i][j][k]==0||judge(i,j,k)) continue;
for(l=0;l<np;++l){
cnt=0;
tmp=prime[l];
if(i+tmp<=n){
++cnt;
dp[i+tmp][j][k]+=dp[i][j][k];
dp[i+tmp][j][k]%=MOD;
}
if(j+tmp<=m){
++cnt;
dp[i][j+tmp][k]+=dp[i][j][k];
dp[i][j+tmp][k]%=MOD;
}
if(k+tmp<=w){
++cnt;
dp[i][j][k+tmp]+=dp[i][j][k];
dp[i][j][k+tmp]%=MOD;
}
if(cnt==0) break;
}
}
}
}
}