学而思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. 在九宫格里面轮流放入 \(1\)\(2\),连成 \(3\) 个就赢了
  2. \(A\)
  3. \(A\) 的第一个子一定是在中心
  4. \(1\) 表示小 \(A\)\(2\) 表示可多,\(0\) 表示没有落子
  5. 保证棋子数不超过 \(3\)
  6. 可多最多只有 \(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;
}
正解思路:
正解代码:

posted @ 2024-07-14 09:59  xlf2011  阅读(8)  评论(0编辑  收藏  举报