YACS-2022.7-丙组

T3  观光电梯

赛时用的贪心算法:先将数组升序排序,把 ans 初始化为等于 4 的人数,

令两个指针 i,j 从数组两端扫描,分 3 种情况:

1. a[ i ] + a[ j ] > 4 则 ans+1,移动左指针,找更小的数与 a[ j ] 组合;

2. a[ i ] + a[ j ] = 4 则 ans+1,两个指针向中间移动,找下一对组合;

3. a[ i ] + a[ j ] < 4 将 a[ i ] 加上 a[ j ],移动右指针,找更大的数与 a[ i ] 组合;

考虑特殊情况:退出循环时,i 和 j 之间还剩下一个数,即 j - i =2,令 ans+1,

用 flag 记录是从上述第几种情况转移来的,方便还原 i 和 j

赛后想到:可以用一个桶 cnt[ ] 记录 1 ~ 4 人的小组出现的次数,再根据 cnt[ i ] 模 4 的结果统计 ans

AC code:

#include <cstdio>
#include <algorithm>
#define putln puts("\n");
using namespace std;

const int maxn=1e5+10;

int n=0,ans=0,t=0;
int a[maxn]={0};
int i=0,j=0;

inline bool cmp(int x,int y){return x>y;}

int flag=0;

int main(){
    while(scanf("%d",&a[n++])!=EOF);n--;j=n-1;

    sort(a,a+n,cmp);

    i=0;while(a[i]==4)i++;ans=i;
    while(i<j){
        if(a[i]+a[j]>4){
            ans++;i++;flag=1;
        }
        else if(a[i]+a[j]==4){
            ans++;i++;j--;flag=2;
        }
        else if(a[i]+a[j]<4){
            a[i]+=a[j];j--;flag=3;
        }
    }
    
    if(flag==1)i--;
    if(flag==2)i--,j++;
    if(flag==3)j++;

    if(j-i==2)ans++;

    printf("%d",ans);

    return 0;
}

T4  匹配括号(三)

方法一:暴力法

思路

可以生成所有 2^2n 个  ‘(’  和  ‘)’  字符构成的序列,然后检查每一个是否有效即可。

算法

为了生成所有序列,可以使用递归。长度为 n 的序列就是在长度为 n−1 的序列前加一个  ‘(’  或  ')'

为了检查序列是否有效,我们遍历这个序列,并使用一个变量 balance 表示左括号的数量减去右括号的数量。如果在遍历过程中 balance 的值小于零,或者结束时 balance 的值不为零,那么该序列就是无效的,否则它是有效的。

复杂度分析

    时间复杂度:O(2^2n * n),对于 2^2n 个序列中的每一个,我们用于建立和验证该序列的复杂度为 O(n)。

    空间复杂度:O(n),除了答案数组之外,我们所需要的空间取决于递归栈的深度,每一层递归函数需要 O(1) 的空间,最多递归 2n 层,因此空间复杂度为 O(n)

方法二:回溯法

思路和算法

方法一还有改进的余地:我们可以只在序列仍然保持有效时才添加  '('  或  ')' ,而不是像方法一那样每次添加。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点,

如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。

时间复杂度分析和方法三参见 剑指 Offer II 085. 生成匹配的括号

这里采用的是方法二

AC code:

#include <cstdio>
#include <vector>
#include <string>
#include <iostream>
using namespace std;

vector<string> ans;
string str;

int cnt=0;

void dfs(string& cur, int open, int close, int n) {
    if(cnt>=1000)return;
    if (cur.size() == n * 2) {
        ans.push_back(cur);cnt++;
        return;
    }
    if (open < n) {
        cur.push_back('(');
        dfs(cur, open + 1, close, n);
        cur.pop_back();
    }
    if (close < open) {
        cur.push_back(')');
        dfs(cur, open, close + 1, n);
        cur.pop_back();
    }
    return;
}

int n=0;

int main(){
    scanf("%d",&n);
    dfs(str, 0, 0, n);
    vector<string>::iterator i;
    for(i=ans.begin();i!=ans.end();i++){
        cout<<*i<<endl;
    }
    return 0;
}

T5 打印六芒星

第一种想法是把这个六芒星当做六个小三角形来做,然后以某种规则排列一下,就能做出来,但是尝试了一下感觉太麻烦了。

第二种想法是,这个图形是对称图形,怎么能写出一半吗? 这样另一半想办法翻转一下就可以了。 尝试了一下,感觉也挺麻烦,逻辑很难捋。

第三种想法是把整个六芒星进行分块,第一块是最上面的三角形,第二块是中间部分,第三块是下面的三角形,然后用三个for循环找到其中的规律,感觉逻辑更清晰了点,但是还是挺麻烦。

第四种想法是发现六芒星可以分为两个大的三角形,我就觉得,就是它了. 第一开始我的想法是先把一个大三角实现出来再说,另一个大三角再用for循环找找规律应该就能输出出来

 

首先这根 6n-5 的线是怎么来的?


从上面的图可以看到,这根线由三个边长为 n 的三角形的边长组成. 所以应该是3n,但是因为中间那个三角形有两个点被覆盖了,所以需要减 2 . 就是 3n - 2.


这是这条线上所有的星星的数量,但是它的星星的排列是一个星星一个空格的形式,所以我们还得把空格计算出来.


我们可以看到左边,中间,右边三个三角形的空格数量都是 n - 1 , 加一起就是 3n - 3
所有加一块就是 6n - 5 这样我们就得到这个边长的 length 了.

知道这个之后,我们就可以求得 mid 的位置,6n - 5一定是一个奇数,所以我们需要考虑这种情况 —— mid = (6n - 5 ) / 2 = 3n - 5 / 2.

这里有一个小数,而int类型会直接把小数抹去,但是我们需要的是进一,所以需要加一.
mid = (6n - 5) / 2 + 1 . 这样我们就知道 mid 的位置了.

AC code:

#include <cstdio>
using namespace std;

const int maxn=5e3+10;

char array[maxn][maxn];

inline void f(int width,int length,int n) {
	int i=0;
	int k=width+n-2;
	int mid1=length/2;
	int mid2=length/2;
	while(i<width&&mid2>=0) {
		array[i][mid1]='*';
		array[i][mid2]='*';
		array[k][mid1]='*';
		array[k][mid2]='*';
		mid1++;
		mid2--;
		i++;
		k--;
	}
	i=0;
	while(i<length) {
		array[n-1][i]='*';
		array[width-1][i]='*';
		i+=2;
	}
    return;
}
inline void print(int width,int length,int n) {
	for(int i=0;i<width+n-1;i++) {
		for(int j=0;j<length;j++) {
			printf("%c",array[i][j]);
		}
		printf("\n");
	}
    return;
}

inline void clear(int width,int length,int n) {
	for(int i=0;i<width+n-1;i++) {
		for(int j=0;j<length;j++) {
			array[i][j]=' ';
		}
	}
    return;
}

int main() {
    int n=0;scanf("%d",&n);
    int length=((n*3)-2)*2-1;
    int width=n*3-2;
    clear(width,length,n);
    f(width,length,n);
    print(width,length,n);
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值