学而思4月月赛总结
概况:
\(T1\) | \(T2\) | \(T3\) | \(T4\) | \(T5\) | \(T6\) | \(T7\) | \(T8\) | \(sum\) |
---|---|---|---|---|---|---|---|---|
\(60\) | \(60\) | \(100\) | \(4.8\) | \(100\) | \(50\) | $ 0$ | \(30\) | \(404.8\) |
未达到满分题:\(1,2,4,6,7,8\)
\(T1\)
题目描述
小 \(A\) 和可多喜欢互相切磋井字棋。井子棋就是在九宫格里面轮流放入 \(1\) 和 \(2\),谁连成 \(3\) 个就赢了。虽然小 \(A\) 很努力地记录棋谱,可还是下不过可多。由于小 \(A\) 比较笨,可多总是让他先。小 \(A\) 下的第一个子一定是在中心。这回小 \(A\) 希望使用计策。目前棋局刚开始,棋盘上有不大于 \(3\) 颗棋子,小 \(A\) 想知道,根据目前的局势,他有没有必胜策略(也就是自己以最佳方案下棋,无论对手怎么下,自己必胜)。
输入描述
共 \(3\)行,表示棋局,\(1\) 表示小 \(A\),\(2\) 表示可多,\(0\) 表示没有落子。保证棋子数不超过 \(3\) 个。
输出描述
共 \(2\) 行。
第 \(1\) 行,如果小 \(A\) 赢,输出 "A will win."
,
如果不知道,输出 "Dont know."
。
第 \(2\) 行,输出棋局刚开始时他们已经下了几个棋子。
样例1
输入
0 0 0
0 1 0
0 0 0
输出
Dont know.
1
样例2
输入
0 0 0
0 1 0
0 2 1
输出
A will win.
3
考场代码 \((60p)\):
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[4][4]={},cnt=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
cin>>a[i][j];
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(a[i][j]!=0)
cnt++;
if(cnt==1||cnt==2) puts("Dont know.");
else if((a[1][1]==1&&a[3][3]==0)||(a[1][3]==1&&a[3][1]==0)||(a[1][1]==0&&a[3][3]==1)||(a[1][3]==0&&a[3][1]==1))
puts("A will win.");
else if((a[1][2]==1&&a[3][2]==0)||(a[2][1]==1&&a[2][3]==0)||(a[1][2]==0&&a[3][2]==1)||(a[2][1]==0&&a[2][3]==1))
puts("A will win.");
else if((a[1][1]==1&&a[3][3]==1)||(a[1][3]==1&&a[3][1]==1)||(a[1][2]==1&&a[3][2]==1)||(a[2][1]==1&&a[2][3]==1))
puts("A will win.");
else puts("Dont know.");
cout<<cnt;
return 0;
}
正解思路:
由题目中可知,我们能够获取到 \(5\) 个直接条件,\(1\) 个推导条件:
- 在九宫格里面轮流放入 \(1\) 和 \(2\),连成 \(3\) 个就赢了
- 小 \(A\) 先
- 小 \(A\) 的第一个子一定是在中心
- \(1\) 表示小 \(A\),\(2\) 表示可多,\(0\) 表示没有落子
- 保证棋子数不超过 \(3\) 个
- 可多最多只有 \(1\) 个棋子
我们可以考虑使用 if语句
判断来解决这个问题,直接且时间复杂度低。所以将所有的 Dont know
通过 if语句
排除,剩下的就是 A will win.
Dont know
情况:
- 只有 \(1\) 个子
- 可多下在角落
- 有一列或一行全都落子
正解代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[5][5],cnt=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
cin>>a[i][j];
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(a[i][j]!=0)
cnt++;
if(cnt==1){
puts("Dont know.");
cout<<1;
return 0;
}
if(a[1][1]==2||a[1][3]==2||a[3][1]==2||a[3][3]==2){
puts("Dont know.");
cout<<cnt;
return 0;
}
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
a[i][4]=a[i][1]+a[i][2]+a[i][3];
a[4][j]=a[1][j]+a[2][j]+a[3][j];
}
if(cnt==3){
for(int i=1;i<=3;i++){
if(a[i][4]==4||a[4][i]==4){
puts("Dont know.");
cout<<3;
return 0;
}
}
}
puts("A will win.");
cout<<cnt;
return 0;
}
\(T2\)
题目描述
学校规定,进校门必须佩戴红领巾。
某一天,可多和同学们共 \(n\) 人出门玩耍。但在回学校时,他们发现只有 \(m\) 个人戴了红领巾。
他们决定,先让 \(m\) 个人戴红领巾进入学校,再派一个人带着所有 \(m\) 条红领巾出来。重复这个过程,直到所有人都回到学校。
假设进出学校都需要花费 \(1\) 个单位的时间,但可以很多人同时进出。
求让所有人都回到学校最少需要花费多少单位时间。
输入描述
输入由多组数据构成。
第一行一个正整数 \(T\),表示共有 \(T\) 组数据。
对于每组数据,有一行两个正整数 \(n\) 和 \(m\),用一个空格隔开,分别表示人数及红领巾的数量。
输出描述
对于每组数据,输出一行一个整数表示答案。
特别地,如果无法让所有人都回到学校,输出 -1
。
样例
输入
3
6 3
10 10
10 1
输出
5
1
-1
考场代码 \((60p)\):
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
if(m==1)
{
puts("-1");
continue;
}
if(n==m)
{
puts("1");
continue;
}
if((n-1)%(m-1)==0) cout<<n/(m-1)*2-1<<endl;
else if(n%(m-1)==0) cout<<n/(m-1)*2-1<<endl;
else cout<<n/(m-1)*2+1<<endl;
}
return 0;
}
正解思路:
什么时候会输出 -1
?只要 \(1\) 轮可以进去至少 \(1\) 个人,就是可行的。但是当红领巾只有 \(1\) 条且人数大于 \(1\) 时,就是不可能的情况,输出 -1
。
接下来要考虑一般情况。可以想到,每一次进去 \(m\) 人,出来 \(1\) 人,耗费 \(2\) 单位的时间。最后肯定还会剩下一些人,\(1\) 单位的时间就可以都进去。最后输出时间即可。
正解代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
if(m==1&&n!=1) puts("-1");
else
{
int t=0;
while(n-m>0)
{
n-=(m-1);
t+=2;
}
t+=1;
cout<<t<<endl;
}
}
return 0;
}
\(T4\)
题目描述
有 \(N\) 颗糖果摆成一排,第 \(i\) 颗糖果的颜色为 \(c_i\),在这一排糖果中,你可以选择其中连续的 \(K\) 个糖果并且获得他们。
你喜欢吃五彩缤纷的糖果,所以你获得的糖果不同颜色越多,就越高兴。
输出你能获得的最多的糖果颜色数
输入描述
第一行两个数字 \(N\) 和 \(K\)。
第二行 \(N\) 个数字,表示每个糖果的颜色 \(c_i\)。
输出描述
一行一个整数,表示你能获得的最多糖果颜色数。
样例1
输入
7 3
1 2 1 2 3 3 1
输出
3
样例2
输入
10 6
1 3 1 3 4 1 2 1 1 2
输出
4
考场代码 \((4.8p)\):
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,k,c[3005],f[3005]={};
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>c[i];
multiset<int> ms;
set<int> s;
for(int i=1;i<=k;i++)
{
s.insert(c[i]);
ms.insert(c[i]);
}
f[k]=s.size();
for(int i=k+1;i<=n;i++)
{
if(ms.count(c[i-k])==1) s.erase(c[i-k]);
ms.erase(ms.find(c[i-k]));
s.insert(c[i]);
ms.insert(c[i]);
f[i]=s.size();
}
int maxn=0;
for(int i=1;i<=n;i++) maxn=max(maxn,f[i]);
cout<<maxn;
return 0;
}
正解思路:
先计算前 \(k\) 个糖果有多少不同的颜色,再利用滑动窗口思想,每次将新一位加入,用桶判断是否已经存在该颜色,最后求出最大值输出。
正解代码:
#include<bits/stdc++.h>
using namespace std;
int c[300005],sm[300005];
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>c[i];
int cur=0,head=1;
for(int i=1;i<=k;i++)
{
sm[c[i]]++;
if(sm[c[i]]==1) cur++;
}
int ans=cur;
for(int i=k+1;i<=n;i++)
{
sm[c[i]]++;
if(sm[c[i]]==1) cur++;
sm[c[head]]--;
if(sm[c[head]]==0) cur--;
head++;
ans=max(ans,cur);
}
cout<<ans;
return 0;
}
\(T6\)
题目描述
卡普有 \(n\) 个整数,他想消灭掉其中的所有负数。他会进行若干次操作,每次选择两个数字,并将它们同时变成它们的乘积。
由于卡普非常聪明,只要这不是不可能的,他就能用最少次数的操作做到这一点。
请你找出他最少操作几次能消灭掉所有负数,或者告诉他这不可能。
输入描述
第一行一个正整数 \(n\)。
第二行 \(n\) 个整数 \(a_{1…n}\),用一个空格隔开,表示初始时的数字。
输出描述
输出一行一个整数表示答案。
如果无解,则输出 -1
。
样例1
输入
5
1 -1 7 2 3
输出
2
样例2
输入
4
3 -1 0 4
输出
1
样例3
输入
1
-7
输出
-1
考场代码 \((50p)\):
#include<bits/stdc++.h>
using namespace std;
int main()
{
bool f0=0;
long long n,c[3005],fs=0;
cin>>n;
for(long long i=1;i<=n;i++)
{
cin>>c[i];
if(c[i]<0) fs++;
if(c[i]==0) f0=1;
}
if(fs%2==0) cout<<fs/2;
else if(f0) cout<<fs/2+1;
else if(fs!=n) cout<<fs*2;
else cout<<-1;
return 0;
}
正解思路:
在输入的同时记录是否有 \(0\) 以及负数的个数。
消灭负数的方法 (设负数个数为 \(w\)):
- 两两相乘 (负数个数为 \(2\) 的倍数时),\(\frac{w}{2}\)
- \(0\) 乘负数 (有 \(0\) 时),\(\frac{w-1}{2}+1\)
- 负数乘整数,再将两个负数相乘 (有正数时),\(\frac{w+1}{2}+1\)
分类讨论输出即可。
正解代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
bool f0=0;
long long n,c[3000005],fs=0;
cin>>n;
for(long long i=1;i<=n;i++)
{
cin>>c[i];
if(c[i]<0) fs++;
if(c[i]==0) f0=1;
}
if(fs%2==0) cout<<fs/2;
else if(fs%2==1&&f0) cout<<(fs-1)/2+1;
else if(fs%2==1&&fs!=n) cout<<(fs+1)/2+1;
else cout<<-1;
return 0;
}
\(T7\)
题目描述
给定 \(n\) 个点和 \(m\) 个关系,每个关系的格式如下:
给出编号 \(x,y\) 和数字 \(a,b\)。假设 \(x\) 的坐标是 \((u,v)\),那么y的坐标是 \((u+a,v+b)\)。
已知1号点的坐标是 \((0,0)\),请你根据这 \(m\) 条关系确定这 \(n\) 个点的坐标。
如果某个点的坐标无法确定,输出 undecidable
。
输入描述
第一行两个整数 \(n,m\)。
接下来 \(m\) 行,每行 \(4\) 个整数表示 \(x,y,a,b\)。
输出描述
\(n\) 行,每行两个整数,第 \(i\) 行的 \(2\) 个整数表示第 \(i\) 个点的坐标。
样例1
输入
3 2
1 2 2 1
1 3 -1 -2
输出
0 0
2 1
-1 -2
样例2
输入
3 2
2 1 -2 -1
2 3 -3 -3
输出
0 0
2 1
-1 -2
样例3
输入
5 7
1 2 0 0
1 2 0 0
2 3 0 0
3 1 0 0
2 1 0 0
3 2 0 0
4 5 0 0
输出
0 0
0 0
0 0
undecidable
undecidable
正解思路:
正解代码:
\(T8\)
题目描述
兰朵在玩自己设计的新游戏,她目前陷入了一场苦战。
战斗的场地由 \(n\) 行,\(m\) 列的地板组成。每块地板上可能是关卡,也可能是城堡的承重柱。
地板上的数字会描述地板状态。如果地板上是一个正整数,就表示这道关卡的怪物数量,如果为 \(0\),表示这块地板是城堡的承重柱。
兰朵可以选择任意一块没有承重柱的地板,然后进行十字切割。
十字切割的效果如下:
- 被选中的地板会拥有十字切割的覆盖范围。
- 从被选中的那块地板开始从上下左右 \(4\) 个方向直线延伸覆盖范围,直到遇到承重柱或者到达战斗场地边界停止。
- 十字切割覆盖范围内的所有怪物都会被击败。
兰朵的体力只允许她使用 \(1\) 次十字切割,她想知道自己最多能击败多少只怪物。
输入描述
第一行两个正整数 \(n\) 和 \(m\),用一个空格分隔,表示战斗场地的大小。
接下来 \(n\) 行,每行输入 \(m\) 个非负整数 \(a_{ij}\),用一个空格分隔,表示第 \(i\) 行,第 \(j\) 列地板上的数字。
输出描述
输出一行一个整数表示答案。
样例1
输入
3 3
1 1 1
1 0 1
0 1 1
输出
5
样例2
输入
5 5
1 9 1 1 9
1 0 1 0 1
0 9 9 9 0
0 1 0 1 1
1 1 1 0 1
输出
31
考场代码 \((30p)\):
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN = 50;
int n, m;
vector<vector<int> > field;
vector<vector<bool> > visited;
int max_kills = 0;
void dfs(int x, int y, int& kills) {
if (x < 0 || x >= n || y < 0 || y >= m || field[x][y] == 0 || visited[x][y]) {
return;
}
visited[x][y] = true;
kills += field[x][y];
dfs(x + 1, y, kills);
dfs(x - 1, y, kills);
dfs(x, y + 1, kills);
dfs(x, y - 1, kills);
}
int main() {
cin >> n >> m;
field.resize(n, vector<int>(m));
visited.resize(n, vector<bool>(m, false));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> field[i][j];
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (field[i][j] != 0) {
int kills = 0;
dfs(i, j, kills);
max_kills = max(max_kills, kills);
}
}
}
cout << max_kills << endl;
return 0;
}
正解思路:
正解代码: